I was trying to write a function that could copy a text file content line by line to a matrix, so this is what I came up with:
#include <stdio.h>
#include <stdlib.h>
#define rows 5
#define columns 12
void file_to_matrix(char *matrix[]){
FILE *file = fopen("swamp.txt", "r");
if(file==NULL){
printf("File error");
exit(1);
}
int y=0;
for(int x=0; (matrix[y][x]=fgetc(file))!=EOF; ++x){
if(matrix[y][x]=='\n'){
++y;
x=0;
}
}
fclose(file);
}
int main(){
char *matrix[rows];
file_to_matrix(matrix);
for(int i=0; i<5; ++i){
for(int j = 0; j < 11; j++){
printf("%c", matrix[i][j]);
}
printf("\n");
}
}
I tried to compile and run it on Windows and Linux but the only thing that it manages to do is giving me errors that are practically useless like:
"Stopping due to fatal error: NullReferenceException: Object reference not set to an instance of an object"
or
"Segmentation fault (core dumped)"
You have to allocate memory for each element of matrix. This is the same exact mistake as writing:
char *s = "string";
As the first act of the program, loop through matrix, and call malloc for each cell:
int i;
for (i = 0; i < 5; ++i)
if (!(matrix[i] = malloc(MAXLEN))) [
perror("malloc");
exit(EXIT_FAILURE);
}
Note: we check the return value of malloc to ensure no errors have occurred.
with your code, i think the matrix in the file will restricted (you define rows=5 and columns=12). so i think again to write a code that can be flexible, no need to set max row or column again. here we go
#include <stdio.h>
#include <stdlib.h> //malloc realloc
//file read function. returning string with splitted new line
//ex: (note: newline is '\n' (ENTER))
//test.txt:
//10 20 30(space)(newline)
//40 50 60(space)(newline)
void file_to_matrix(char *filename)
{
FILE * file = fopen(filename, "r");
char *buff = (char*)malloc(sizeof(char)); //temp string
int *matrix_row = (int*)malloc(sizeof(int)); //temp matrix row
int **MATRIX = (int**)malloc(sizeof(int*)); //this will become the final matrix
int index_buff = 0, index_MATRIX = 0, index_matrix_row = 0;
while (1)
{
char c = getc(file);
if (c==EOF)
{
break;
}
if (c=='\n') //if c meet newline, then add the matrix row to final matrix
{
(*(MATRIX+index_MATRIX)) = matrix_row;
index_MATRIX++;
MATRIX = realloc(MATRIX, sizeof(MATRIX)*(index_MATRIX+1));
matrix_row = (int*)malloc(sizeof(int));
index_matrix_row = 0;
printf("\n");
continue;
}
if (c==' ') //if c meet space, then buff wil converted to int
{
int num = atoi(buff);
printf("%d ", num);
(*(matrix_row+index_matrix_row)) = num;
index_matrix_row++;
matrix_row = realloc(matrix_row, sizeof(int)*(index_matrix_row+1));
free(buff);
buff = (char*)malloc(sizeof(char));
index_buff=0;
continue;
}
//add buff with c
(*(buff+index_buff))=c;
index_buff++;
buff = realloc(buff, sizeof(char)*(index_buff+1));
}
fclose(file);
//clearing the dynamic memory
free(buff);
for (int i = 0; i < index_MATRIX; i++)
{
free(MATRIX[i]);
}
}
int main()
{
file_to_matrix("test.txt");
return 0;
}
test.txt:
10 20 30
40 50 60
70 80 90
if you confused with the test.txt format, i will explain again:
10 20 30(space)(newline)
30 40 60(space)(newline)
70 80 90(space)(newline)
Thank you for your replies, I think your solutions are very clever but I was looking to an easier one.
After a little bit of trial and error I think I found a solution that it isn't very generalized but it's simple and it seems to work fine:
#include <stdlib.h>
#include <stdio.h>
#define ROWS <n of rows>
#define COLS <n of columns>
void file_to_matrix(int rows, int cols, char matrix[rows][cols], char file_path[]){
FILE *file = fopen(file_path, "r");
if(file==NULL){
printf("File error");
exit(1);
}
int y=0;
for(int x=0; (matrix[y][x]=getc(file))!=EOF; ++x){
if(matrix[y][x]=='\n'){
matrix[y][x]='\0';
++y;
x=-1;
}
}
fclose(file);
}
int main(){
char matrix[ROWS][COLS+2];
file_to_matrix(ROWS, COLS+2, matrix, "file path");
for(int i=0; i<=ROWS; ++i){
printf("%s\n", matrix[i]);
}
}
Thank you again for your answers
Related
I have a text file I want to upload into a pointer array but I can't find any references besides 2D arrays or languages other than C.
My input.txt:
marbels
fruit
vegetables
marshmellow sprinkle
coffe beans
My source.c (my question is located in the int main(void){})
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define MAX_LEN 1000
void sort(size_t size, char* data[]) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (strlen(data[j]) < strlen(data[j + 1])) {
char* temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
void printArray(size_t size, char* data[]) {
for (int i = 0; i < size; i++) {
printf("%s ", data[i]); //%c -> %s
}
}
int main(void) {
char* data[1000]; //I want the array to hold maximum 1000 characters
FILE* file;
file = fopen("C:\\Users\\EXAMPLE\\desktop\\input.txt", "r");
if (file == NULL) {
printf("File Error\n", file);
return 1;
}
//between these dashes in my issue with uploading the text from input to the char* data[1000];
int line = 0;
//if i build the program with this while-loop, I get an error
while (!eof(file) && ferror(file)) {
if (fgets(data[line], MAX_LEN, file) != NULL) { //error C6001: using uninitialized memory 'data'
line++;
}
}
fclose(file);
//
size_t size = sizeof data / sizeof data[0];
sort(size, data);
printArray(size, data);
return 0;
}
The error message:
I tried that while loop before and it wasn't great but only thing I could find at the time for this project.
As you have mentioned in your question, you will need to use a 2D array or otherwise you can use the malloc() function.
In your code you have a array with 1000 * sizeof(char) bytes. So bassically you have 1000 uninitialised pointers. But there are no pointers allocated that are able to store the lines of the file.
Attempting to create a program that reasons in a large Text File and filled them into Rows + Columns. Eventually I'll have to computer best path but having trouble just implementing an Array that can store the values.
#include <stdio.h>
#include <stdlib.h>
//max number of characters to read in a line. //MAXN=5 means 4 chars are read, then a \0 added in fgets. See reference for functionality
#define MAXN 100L
int main(void) //char** argv also ok {
int i=0, totalNums, totalNum,j=0;
size_t count;
int numbers[100][100];
char *line = malloc(100);
FILE* inFile ;
inFile = fopen("Downloads/readTopoData/topo983by450.txt", "r"); //open a file from user for reading
if( inFile == NULL) { // should print out a reasonable message of failure here
printf("no bueno \n");
exit(1);
}
while(getline(&line,&count, inFile)!=-1) {
for(;count>0; count--,j++)
sscanf(line, "%d", &numbers[i][j]);
i++;
}
totalNums = i;
totalNum = j;
for(i=0;i<totalNums; i++){
for(j=0;j<totalNum;j++){
printf("\n%d", numbers[i][j]);
}
}
fclose(inFile);
return 0;
}
count does not tell you how many numbers there are. Further: sscanf(line, "%d", &numbers[i][j]); will just scan the same number every time.
So this
for(;count>0; count--,j++)
sscanf(line, "%d", &numbers[i][j]);
should be something like:
j = 0;
int x = 0;
int t;
while(sscanf(line + x, "%d%n", &numbers[i][j], &t) == 1)
{
x += t;
++j;
}
where x together with %n helps you move to a new position in the string when a number has been scanned.
Here is a simplified version that scans for numbers in a string:
#include <stdio.h>
int main(void) {
char line[] = "10 20 30 40";
int numbers[4];
int j = 0;
int x = 0;
int t;
while(j < 4 && sscanf(line + x, "%d%n", &numbers[j], &t) == 1)
{
x += t;
++j;
}
for(t=0; t<j; ++t) printf("%d\n", numbers[t]);
return 0;
}
Output:
10
20
30
40
I have a .csv file that reads like:
SKU,Plant,Qty
40000,ca56,1245
40000,ca81,12553.3
40000,ca82,125.3
45000,ca62,0
45000,ca71,3
45000,ca78,54.9
Note: This is my example but in reality this has about 500,000 rows and 3 columns.
I am trying to convert these entries into a 2D array so that I can then manipulate the data. You'll notice that in my example I just set a small 10x10 matrix A to try and get this example to work before moving on to the real thing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char *getfield(char *line, int num);
int main() {
FILE *stream = fopen("input/input.csv", "r");
char line[1000000];
int A[10][10];
int i, j = 0;
//Zero matrix
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
A[i][j] = 0;
}
}
for (i = 0; fgets(line, 1000000, stream); i++) {
while (j < 10) {
char *tmp = strdup(line);
A[i][j] = getfield(tmp, j);
free(tmp);
j++;
}
}
//print matrix
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
printf("%s\t", A[i][j]);
}
printf("\n");
}
}
const char *getfield(char *line, int num) {
const char *tok;
for (tok = strtok(line, ",");
tok && *tok;
tok = strtok(NULL, ",\n"))
{
if (!--num)
return tok;
}
return 0;
}
It prints only "null" errors, and it is my belief that I am making a mistake related to pointers on this line: A[i][j] = getfield(tmp, j). I'm just not really sure how to fix that.
This is work that is based almost entirely on this question: Read .CSV file in C . Any help in adapting this would be very much appreciated as it's been a couple years since I last touched C or external files.
It looks like commenters have already helped you find a few errors in your code. However, the problems are pretty entrenched. One of the biggest issues is that you're using strings. Strings are, of course, char arrays; that means that there's already a dimension in use.
It would probably be better to just use a struct like this:
struct csvTable
{
char sku[10];
char plant[10];
char qty[10];
};
That will also allow you to set your columns to the right data types (it looks like SKU could be an int, but I don't know the context).
Here's an example of that implementation. I apologize for the mess, it's adapted on the fly from something I was already working on.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Based on your estimate
// You could make this adaptive or dynamic
#define rowNum 500000
struct csvTable
{
char sku[10];
char plant[10];
char qty[10];
};
// Declare table
struct csvTable table[rowNum];
int main()
{
// Load file
FILE* fp = fopen("demo.csv", "r");
if (fp == NULL)
{
printf("Couldn't open file\n");
return 0;
}
for (int counter = 0; counter < rowNum; counter++)
{
char entry[100];
fgets(entry, 100, fp);
char *sku = strtok(entry, ",");
char *plant = strtok(NULL, ",");
char *qty = strtok(NULL, ",");
if (sku != NULL && plant != NULL && qty != NULL)
{
strcpy(table[counter].sku, sku);
strcpy(table[counter].plant, plant);
strcpy(table[counter].qty, qty);
}
else
{
strcpy(table[counter].sku, "\0");
strcpy(table[counter].plant, "\0");
strcpy(table[counter].qty, "\0");
}
}
// Prove that the process worked
for (int printCounter = 0; printCounter < rowNum; printCounter++)
{
printf("Row %d: column 1 = %s, column 2 = %s, column 3 = %s\n",
printCounter + 1, table[printCounter].sku,
table[printCounter].plant, table[printCounter].qty);
}
// Wait for keypress to exit
getchar();
}
There are multiple problems in your code:
In the second loop, you do not stop reading the file after 10 lines, so you would try and store elements beyond the end of the A array.
You do not reset j to 0 at the start of the while (j < 10) loop. j happens to have the value 10 at the end of the initialization loop, so you effectively do not store anything into the matrix.
The matrix A should be a 2D array of char *, not int, or potentially an array of structures.
Here is a simpler version with an allocated array of structures:
#include <stdio.h>
#include <stdlib.h>
typedef struct item_t {
char SKU[20];
char Plant[20];
char Qty[20];
};
int main(void) {
FILE *stream = fopen("input/input.csv", "r");
char line[200];
int size = 0, len = 0, i, c;
item_t *A = NULL;
if (stream) {
while (fgets(line, sizeof(line), stream)) {
if (len == size) {
size = size ? size * 2 : 1000;
A = realloc(A, sizeof(*A) * size);
if (A == NULL) {
fprintf(stderr, "out of memory for %d items\n", size);
return 1;
}
}
if (sscanf(line, "%19[^,\n],%19[^,\n],%19[^,\n]%c",
A[len].SKU, A[len].Plant, A[len].Qty, &c) != 4
|| c != '\n') {
fprintf(stderr, "invalid format: %s\n, line);
} else {
len++;
}
}
fclose(stream);
//print matrix
for (i = 0; i < len; i++) {
printf("%s,%s,%s\n", A[i].SKU, A[i].Plant, A[i].Qty);
}
free(A);
}
return 0;
}
Im tryin to read a ascii-file consisting of 2 columns of and a variable nr of rows. Reading is done by processor 0 and then the data is distributed using MPI_Bcast(...). I had to create a 1-dim buffer array which contains the data and is sent to all other procs because I haven't found a way to directly broadcast the 2-dim. data.
There's something wrong with the allocation of the array arr. When I try to print the data, the first 3 rows are printed and then I get a segmentation fault. Most likely there are several mistakes.
I really tried to make the example as simple as possible.
my code:
//global variables
...
double **testarray;
int dim;
...
int main(int argc, char **argv){
...
...
read_file("test.txt",&testarray,&dim);
}
int read_file(char* infilename,double ***arr,int *rowsout)
{
FILE* infile;
int i,j,ch,number_of_lines=0;
int rows=0;
//first count lines
if(myid==0) //myid is processor id
{
infile = fopen(infilename, "r");
do
{
ch = fgetc(infile);
if(ch == '\n')
number_of_lines++;
} while (ch != EOF);
if(ch != '\n' && number_of_lines != 0)
number_of_lines++;
//close file
fclose(infile);
rows=number_of_lines-1;
*rowsout=rows;
}
// every proc should know about length of file in order
//to be able to allocate memory
MPI_Bcast(rowsout,1,MPI_INT,0,MPI_COMM_WORLD);
//allocate memory
double *buf; //1D-buffer for 2D-array
MPI_Alloc_mem((*rowsout)*2*sizeof(double), MPI_INFO_NULL, &buf);
MPI_Alloc_mem((*rowsout)*sizeof(double*), MPI_INFO_NULL,arr);
for (i = 0; i < (*rowsout); i++) {
MPI_Alloc_mem(2*sizeof(double),MPI_INFO_NULL,&arr[i]);
}
// Now read file on proc 0
if(myid==0)
{
infile=fopen(infilename,"r");
for(i=0;i<rows;i++)
{
for(j=0;j<2;j++)
{
fscanf(infile,"%lf",arr[i][j]);
printf("arr[%d][%d]:%e\n",i,j,(*arr)[i][j]);
}
}
fclose(infile);
}
return 0;
//dont go further, error occurs before loop finishs
MPI_Bcast(buf,(rows)*2,MPI_DOUBLE,0,MPI_COMM_WORLD);
//now reconstruct array from buffer
for(i=0;i<(*rowsout);i++)
{
for(j=0;j<2;j++)
{
*arr[i][j]=buf[i*2+j];
}
}
MPI_Free_mem(buf);
return 0;
}
You have some issues with your arr as you guessed. Here is a fixed version of your code (minus the MPI stuff):
#include <stdio.h>
#include <stdlib.h>
void read_file(char *filename, double ***arr, int *rowsout);
int main(void)
{
double **testarray;
int dim;
read_file("test.txt", &testarray, &dim);
return 0;
}
void read_file(char *filename, double ***arr, int *rowsout)
{
FILE *infile;
int i, j, ch;
infile = fopen(filename, "r");
do
{
ch = fgetc(infile);
if (ch == '\n')
++*rowsout;
} while (ch != EOF);
rewind(infile);
*arr = malloc(sizeof **arr * *rowsout);
for (i = 0; i < *rowsout; ++i)
(*arr)[i] = malloc(sizeof ***arr * 2);
for (i = 0; i < *rowsout; ++i)
{
for (j = 0; j < 2; ++j)
{
fscanf(infile, "%lf", &(*arr)[i][j]);
printf("(*arr)[%d][%d]: %e\n", i, j, (*arr)[i][j]);
}
}
fclose(infile);
for (i = 0; i < *rowsout; ++i)
free((*arr)[i]);
free(*arr);
}
Example input: test.txt
0.01 0.02
0.3 0.4
5.0 6.0
Example output:
(*arr)[0][0]: 1.000000e-02
(*arr)[0][1]: 2.000000e-02
(*arr)[1][0]: 3.000000e-01
(*arr)[1][1]: 4.000000e-01
(*arr)[2][0]: 5.000000e+00
(*arr)[2][1]: 6.000000e+00
I believe the mistake comes with your fscanf() line. fscanf() wants a double * when you are using "%lf", but you are instead passing arr[i][j] and there's two things wrong with that: since arr is actually a pointer to your 2-D array, you will need to deference it first ((*arr)), and second, since it needs the address of the double, you will need to use &: &(*arr)[i][j].
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main()
{
int i = 0, len1=0, len2=0;
int BUFSIZE = 1000;
char* string1[20];
char* string2[20];
FILE *fp1 = fopen("input1.txt", "r");
FILE *fp2 = fopen("input2.txt", "r");
if ((fp1 == 0)||(fp2 == 0)){
fprintf(stderr, "Error while opening");
return 0;
}
string1[i] = (char*)malloc(BUFSIZE);
string2[i] = (char*)malloc(BUFSIZE);
while (fgets(string1[i], BUFSIZE, fp1)) {
i++;
len1+=strlen(string[i]);
string1[i] = (char*)malloc(BUFSIZE);
len1+=strlen(string1[i]);
}
i = 0;
while (fgets(string2[i], BUFSIZE, fp2)) {
i++;
string2[i] = (char*)malloc(BUFSIZE);
}
printf("Output: \n");
srand(time(NULL));
int j = rand()%i;
int k = (j+1)%i;
fflush(stdout);
printf("%d - %s %d -%s", j, string1[j], k, string1[k]);
printf("%d - %s %d -%s", j, string2[j], k, string2[k]);
printf("\n");
printf("%d", len1);
int x;
for(x = 0; x<i; x++){
free(string1[x]);
free(string2[x]);
}
scanf("%d", x);
fclose(fp1);
fclose(fp2);
return 0;
}
Thanks to user1807597's help, I finally realize reading two strings and storing into arrays. But I still have trouble in getting the length of the array, I try to put len+=strlen(string[i]); in the while loop, but the compiler breaks when debugging. Someone knows why? Thank you!
You have two main choices for the length of the array.
You make it big enough that you don't think you'll ever use more entries.
enum { MAX_LINES = 16 * 1024 };
char *lines[MAX_LINES];
size_t num_lines = 0;
You do dynamic allocation of the array.
char **lines = 0;
size_t max_lines = 0;
size_t num_lines = 0;
...
if (num_lines >= max_lines)
{
size_t new_lines = max_lines * 2 + 2;
char **space = realloc(lines, new_lines * sizeof(*space));
if (space == 0)
...deal with out of memory...
lines = space;
max_lines = new_lines;
}
Both systems work. Dynamic allocation is a little more fiddly the first few times you do it, but you don't run into problems until you run out of memory, and it takes a long time to run out of memory these days. Fixed allocation may run out of space if you guess wrong, and nominally wastes memory if you only deal with small files. The 'wasted' memory is usually very small these days.
Which is better depends on the scale of the problems you expect your program to deal with. If they'll be small, a fixed but generous allocation is sensible and easier. If they'll be large, the dynamic allocation is more sensible.
I have modified the code.I think now it's ok.You have wrong counting in the first while loop.And i++ should come after len1+=strlen(string[i]);.In the last scanf statement scanf("%d", x);,you have missed the operator '&'.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main()
{
int i = 0, len1 = 0, len2 = 0;
int BUFSIZE = 1000;
/*
you have too few lines here,
If my file contains more than 20 lines,tragedy will occur!
*/
char* string1[20];
char* string2[20];
FILE* fp1 = fopen("input1.txt", "r");
FILE* fp2 = fopen("input2.txt", "r");
if ((fp1 == 0) || (fp2 == 0))
{
fprintf(stderr, "Error while opening");
return 0;
}
string1[i] = (char*)malloc(BUFSIZE);
string2[i] = (char*)malloc(BUFSIZE);
while (fgets(string1[i], BUFSIZE, fp1)!=NULL)
{
/*
i++;
*/
len1 += strlen(string1[i]);
i++;
string1[i] = (char*)malloc(BUFSIZE);
}
i = 0;
while (fgets(string2[i], BUFSIZE, fp2))
{
i++;
string2[i] = (char*)malloc(BUFSIZE);
}
printf("Output: \n");
srand(time(NULL));
int j = rand() % i;
int k = (j + 1) % i;
fflush(stdout);
printf("%d - %s %d -%s", j, string1[j], k, string1[k]);
printf("%d - %s %d -%s", j, string2[j], k, string2[k]);
printf("\n");
printf("%d", len1);
int x;
for (x = 0; x < i; x++)
{
free(string1[x]);
free(string2[x]);
}
/*
scanf("%d", x);
*/
scanf("%d",&x);
fclose(fp1);
fclose(fp2);
return 0;
}