I have a text file with the given format:
2078.62 5.69982 -0.17815 -0.04732
5234.95 8.40361 0.04028 0.10852
2143.66 5.35245 0.10747 -0.11584
7216.99 2.93732 -0.18327 -0.20545
1687.24 3.37211 0.14195 -0.14865
2065.23 34.0188 0.1828 0.21199
2664.57 2.91035 0.19513 0.35112
7815.15 9.48227 -0.11522 0.19523
5166.16 5.12382 -0.29997 -0.40592
6777.11 5.53529 -0.37287 -0.43299
4596.48 1.51918 -0.33986 0.09597
6720.56 15.4161 -0.00158 -0.0433
2652.65 5.51849 0.41896 -0.61039
I have written the following functions to read the file
Read the number of lines
unsigned int getnumline(const char *sn){
unsigned int n;
char lcstring[LCLENGTH];
FILE *lcpipe;
char buff[512];
snprintf( lcstring, LCLENGTH,
"wc -l %s | cut -d ' ' -f1", sn);
lcpipe = popen( lcstring, "r" );
if (lcpipe == NULL)
exit_failure( "popen: " );
while(fgets(buff, sizeof(buff), lcpipe)!=NULL){
n=atoi(buff);
}
pclose(lcpipe);
printf("Number of lines in the input file: %d\n", n);
return n;
}
Read the text file
double **callocmatrix( unsigned int m, unsigned int n ) {
double **matrix;
unsigned int i;
matrix = (double **)calloc(m, sizeof(double *));
if ( !matrix )
return NULL;
matrix[0] = (double *)calloc(m*n, sizeof(double));
if ( !matrix[0] )
return NULL;
for ( i = 1; i < m; i += 1 )
matrix[i] = matrix[i-1] + n;
return matrix;
}
void freematrix( double **matrix ) {
free( (void *)matrix[0] );
}
double **ellcat;
ngal = getnumline(sname);
ellcat = callocmatrix( ngal, 4 );
void readcat( double **ellcat, unsigned int catlen, const char *sn ) {
unsigned int i;
FILE *fp=fopen(sn,"r");
if(fp == NULL)
{
printf("Error in opening file\n");
exit(0);
}
for (i=0 ; i< catlen ; i++)
{
fscanf(fp, "%lf %lf %lf %lf", &ellcat[i][0], &ellcat[i][1], &ellcat[i][2], &ellcat[i][3]);
}
for (i=0 ; i< catlen ; i++)
{
printf("x = %lf, y = %lf, e1 = %lf, e2 = %lf\n", &ellcat[i][0], &ellcat[i][1], &ellcat[i][2], &ellcat[i][3]);
}
fclose(fp);
}
But the ellcat
components are empty
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
x = 0.000000, y = 0.000000, e1 = 0.000000, e2 = 0.000000
I can not figure out what the problem is with my readcat
function.
PS I have to mention I am a newbie with respect to the C language.
Firstly, using an external program to count the number of lines is a bit awkward. It would be more efficient (involving less code, and less system resources) to simply count the number of newlines in the file:
size_t get_newline_count(char const *f) {
size_t newline_count = 0;
FILE *fd = fopen(f, "r");
if (!fd) {
return 0;
}
int c;
do {
c = fgetc(fd);
newline_count += (c == '\n');
} while (c >= 0);
fclose(fd);
return newline_count;
}
unsigned int n;
...
printf("Number of lines in the input file: %d\n", n);
The return type of the function I wrote above is size_t
, so you'll want to use the %zu
format specifier to print it. When the type you want to print is unsigned int
(as in your code), use %u
; if you supply the wrong type of argument ( %d
corresponds to int
), the behaviour is undefined.
fscanf(fp, "%lf %lf %lf %lf", &ellcat[i][0], &ellcat[i][1], &ellcat[i][2], &ellcat[i][3]);
Future note : It is your responsibility to give us a complete MCVE that compiles and reproduces the problem without having to fill in any blanks .
You haven't given us the declaration for ellcat
, so we can only speculate that you may have given the wrong type (since you did precisely that above). ellcat
needs to have two levels of indirection referring to a double
for this code to be correct.
printf("x = %lf, y = %lf, e1 = %lf, e2 = %lf\n", &ellcat[i][0], &ellcat[i][1], &ellcat[i][2], &ellcat[i][3])
Finally, as another answer has indicated, when using printf
to print double
(or float
, the l
in %lf
is redundant in this case due to default promotion to double
), "You don't need to pass address of variables"... Indeed, you shouldn't pass an address, because that would mean you're providing the incorrect type, which is undefined behaviour as we covered earlier.
While printing values , in printf
-
printf("x = %lf, y = %lf, e1 = %lf, e2 = %lf\n",
&ellcat[i][0], &ellcat[i][1], &ellcat[i][2], &ellcat[i][3]);
^ ^ ^ ^
You don't need to pass address of variables . Just pass them . Remove the &
operator.
Unless there is a requirement that you pre-read to determine the number of lines in the file before allocating space/reading the values into your array, there is no reason to do it. You can easily read values from your file, allocate space to hold them, and keep track of the number of lines of data read in a single function/read loop. In fact, you make this quite easy by using a pointer to pointer to type (double pointer) as your data type.
The normal approach in your situation is to allocate memory for some reasonably anticipated number of lines (pointers) and then to read/allocate space for each line of data until you reach this original limit, and then to realloc
additional pointers as required to read the entire file into your array of pointers to array of doubles. The line count is passed an a pointer argument to the function and updated within the function making the total number of lines available back in the calling function ( main()
here).
An example of one function that would meet your requirements would be:
/* read rows in filename 'fn' that consist of 4 whitespace
* separated double values into the dynamically allocated
* array of pointer to array of 4 doubles 'ar', updating the
* size_t value 'nrows' with the number of rows of data read.
* returns pointer to allocated array on success, NULL otherwise
*/
double **readcat (double ***ar, size_t *nrow, const char *fn)
{
double tmp[4] = {0.0}; /* tmp array for values */
FILE *fp = fn ? fopen (fn, "r") : stdin; /* read from fn or stdin */
if (!fp) {
fprintf (stderr, "readcat() error: file open failed '%s'.\n", fn);
return NULL;
}
/* allocate MAXR pointers to double* */
if (!(*ar = calloc (MAXR, sizeof **ar))) {
fprintf (stderr, "readcat() error: virtual memory exhausted.\n");
return NULL;
}
*nrow = 0; /* set index to 0, read each row in file */
while (fscanf (fp, "%lf %lf %lf %lf",
&tmp[0], &tmp[1], &tmp[2], &tmp[3]) == 4) {
/* 4 values read, allocate memory in *ar */
if (!((*ar)[*nrow] = calloc (4, sizeof ***ar))) {
fprintf (stderr, "readcat() error: virtual memory exhausted.\n");
return NULL;
}
/* copy values to *ar */
memcpy ((*ar)[*nrow], tmp, sizeof tmp);
(*nrow)++; /* increment row index */
if (*nrow == MAXR) { /* test rows againt max (break/realloc) */
fprintf (stderr, "readcat() warning: MAXR rows read.\n");
break;
}
}
if (fp != stdin) fclose (fp); /* close file */
return *ar;
}
Note, the reallocation code is omitted above and the read loop is exited if the maximum number of pointers (lines) MAXR
is reached. Also note, that by returning a pointer, you not only can check success/failure based on the return, you also have the option of assigning the return to a pointer.
A short driver program making use of the function with your data would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXR 64
double **readcat (double ***ar, size_t *nrow, const char *fn);
int main (int argc, char **argv) {
size_t i, n = 0;
double **ellcat = NULL;
char *filename = argc > 1 ? argv[1] : NULL;
ellcat = readcat (&ellcat, &n, filename);
if (!ellcat) { /* validate address returned by readcat */
fprintf (stderr, "error: readcat returned no values.\n");
exit (EXIT_FAILURE);
}
printf ("\n %zu rows of data read:\n\n", n);
for (i = 0; i < n; i++) /* print each row of data */
printf (" %8.2lf %8.5lf %8.5lf %8.5lf\n",
ellcat[i][0], ellcat[i][1], ellcat[i][2], ellcat[i][3]);
putchar ('\n');
for (i = 0; i < n; i++) /* free all allocated memory */
free (ellcat[i]);
free (ellcat);
return 0;
}
Use/Output
$ ./bin/fscanf_4col_dyn dat/float_4col.txt
13 rows of data read:
2078.62 5.69982 -0.17815 -0.04732
5234.95 8.40361 0.04028 0.10852
2143.66 5.35245 0.10747 -0.11584
7216.99 2.93732 -0.18327 -0.20545
1687.24 3.37211 0.14195 -0.14865
2065.23 34.01880 0.18280 0.21199
2664.57 2.91035 0.19513 0.35112
7815.15 9.48227 -0.11522 0.19523
5166.16 5.12382 -0.29997 -0.40592
6777.11 5.53529 -0.37287 -0.43299
4596.48 1.51918 -0.33986 0.09597
6720.56 15.41610 -0.00158 -0.04330
2652.65 5.51849 0.41896 -0.61039
Memory Error/Leak Check
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory and to confirm that you have freed all the memory you have allocated. For Linux valgrind
is the normal choice. There are so many subtle ways to misuse a block of memory that can cause real problems, there is no excuse not to do it. There are similar memory checkers for every platform. They are all simple to use. Just run your program through it.
$ valgrind ./bin/fscanf_4col_dyn dat/float_4col.txt
==21392== Memcheck, a memory error detector
==21392== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==21392== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==21392== Command: ./bin/fscanf_4col_dyn dat/float_4col.txt
==21392==
13 rows of data read:
2078.62 5.69982 -0.17815 -0.04732
<snip>
2652.65 5.51849 0.41896 -0.61039
==21392==
==21392== HEAP SUMMARY:
==21392== in use at exit: 0 bytes in 0 blocks
==21392== total heap usage: 15 allocs, 15 frees, 1,496 bytes allocated
==21392==
==21392== All heap blocks were freed -- no leaks are possible
==21392==
==21392== For counts of detected and suppressed errors, rerun with: -v
==21392== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
打印时不需要提供地址,将其更改为
printf("x = %lf, y = %lf, e1 = %lf, e2 = %lf\n",ellcat[i][0],ellcat[i][1],ellcat[i][2],ellcat[i][3]);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.