allocate and fill 2d-array in a function using mpi in C - c

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].

Related

Array elements are "lost" outside the function

I have a matrix in a file like:
3
1 2 3
4 5 6
7 8 -9
where the first line indicates the square matrix order. I'm using the following code to read the file and store it into a vector (I have removed all if checks for sake of simplicity):
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int read_matrix_file(const char *fname, double *vector)
{
/* Try to open file */
FILE *fd = fopen(fname, "r");
char line[BUFSIZ];
fgets(line, sizeof line, fd);
int n;
sscanf(line, "%d", &n)
vector = realloc(vector, n * n * sizeof *vector);
memset(vector, 0, n * n * sizeof *vector);
/* Reads the elements */
int b;
for(int i=0; i < n; i++) {
// Read the i-th line into line
if (fgets(line, sizeof line, fd) == NULL) {
perror("fgets");
return(-1);
}
/* Reads th j-th element of i-th line into the vector */
char *elem_ptr = line;
for (int j=0; j < n; j++) {
if(sscanf(elem_ptr, "%lf%n", &vector[n*i+j] , &b) != 1) {
perror("sscanf");
return(1);
}
elem_ptr += b;
}
}
fclose(fd);
/* HERE PRINTS OK */
for(int i=0; i<n*n; i++)
printf("%i %f\n",i, vector[i]);
return n;
}
The read_matrix_file receives a filename and an array of doubles and fill the array, returning the matrix order. The expected usage can be seen in this code block.
int main(void)
{
const char *fname = "matrix.txt";
double *vector = malloc(sizeof * vector);
int n = read_matrix_file(fname, vector);
/* Here prints junk */
for(int i=0; i<n*n; i++)
printf("%i %f\n",i, vector[i]);
free(vector);
}
The issue is, the printf works fine inside the read_matrix_file but seems invalid in main.
I'm allocating the array outside the function and passing it by "reference", but I'm very suspecious of realloc, unfortunatelly I don't know how to fix or a better approach.
You are reallocating memory inside read_matrix_file() and storing the elements of the matrix in that memory region. But when you come out of the function, since the pointer vectoris a local variable, its new value is lost when you leave the function.
When you come back inside main() vector still points to the (now likely invalid) memory region that you had previously allocated with malloc().
You should either allocate large enough memory before calling read_matrix_file or pass a double pointer (**) if you want to modify the pointer and see the change reflected back in main()
What I meant is something like this:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int read_matrix_file(const char *fname, double **p_vector)
{
/* Try to open file */
FILE *fd = fopen(fname, "r");
char line[BUFSIZ];
fgets(line, sizeof line, fd);
int n;
sscanf(line, "%d", &n);
*p_vector = realloc(*p_vector, n * n * sizeof **p_vector);
double *vector = *p_vector;
memset(vector, 0, n * n * sizeof *vector);
/* Reads the elements */
int b;
for(int i=0; i < n; i++) {
// Read the i-th line into line
if (fgets(line, sizeof line, fd) == NULL) {
perror("fgets");
return(-1);
}
/* Reads th j-th element of i-th line into the vector */
char *elem_ptr = line;
for (int j=0; j < n; j++) {
if(sscanf(elem_ptr, "%lf%n", &vector[n*i+j] , &b) != 1) {
perror("sscanf");
return(1);
}
elem_ptr += b;
}
}
fclose(fd);
/* HERE PRINTS OK */
for(int i=0; i<n*n; i++)
printf("%i %f\n",i, vector[i]);
return n;
}
In main, call it with:
int n = read_matrix_file(fname, &vector);
EDIT: Please note that this code does not handle the failure of realloc() properly.

Reading file content line by line into a matrix

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

Filling an array of strings with the content of a file

I wrote a short code that should copy the content of a file into an initialized array of strings and then print that array. I get no errors/warnings but still the program doesn't print anything when i run it. The code is the following:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
const int dim = 30;
int main() {
char* arr[dim];
int i = 0;
FILE* fp;
fp = fopen("test.txt", "r+");
if(fp == NULL) {
printf("\nError, breaking...");
return 0;
}
while(i <= dim) {
arr[i] = (char *)malloc(dim*sizeof(char *));
++i;
}
i = 0;
while(fscanf(fp, "%s", arr[i]) != EOF) {
printf("%s: added\n", arr[i]);
++i;
}
}
The file contains a series of words separated only by whitespaces and newline characters.
correct followings and see if it helps
1) initialise i to 0 ie. i=0;
2)
while(i < dim) { } // it should be < as array start with 0

Reading in Large Integer txt file into 2D array

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

Parsing .csv file into 2D array in C

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;
}

Resources