I am trying to think of logic on how to read the last line of a file but I cannot come up with answers. ACCOUNTNUM info is a structure. my .dat file who has 3 acc numbers already. Here I want to know how to get the last account number/line which is 2022-3.
This is the function
LastAccountNumber(){
ACCOUNTNUM info;
file = fopen("acctNumbers.dat","r");
char firstAcc[50]="2022-1";//ignore this I was gonna compare this with the last line.
while((fread(&info,sizeof(struct accountNum),1,file))==1){
printf("\nAcc No %s",info.sAccountNum);
}
}
}
This is my structure
struct accountNum{
int sequence;
char sAccountNum[16];
};
typedef struct accountNum ACCOUNTNUM;
Content of my acctNumbers.dat file.
2022-1 ú· 2022-2 ú· 2022-3 ú·
You could call fread in a loop until it returns 0, and then print the last record read:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct accountNum
{
int sequence;
char sAccountNum[16];
} ACCOUNTNUM;
int main( void )
{
FILE *fp;
ACCOUNTNUM info;
bool success = false;
//open file and verify that it is open
fp = fopen( "acctNumbers.dat", "r" );
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
//skip to last record
while( ( fread( &info, sizeof info, 1, fp) ) == 1 )
{
//set variable to indicate that we have found at
//least one record
success = true;
}
//print last record, if it exists
if ( success )
printf( "Acc No: %s\n", info.sAccountNum );
else
printf( "No records found.\n" );
//cleanup
fclose( fp );
}
However, this is only guaranteed to work if you are sure that the last fread will not perform a partial read, i.e. if you are sure that the file size is an exact multiple of sizeof(ACCOUNTNUM). Because if fread does perform a partial read, then the buffer content will be indeterminate.
If you cannot exclude the possibility of a partial read, then you could use two buffers for reading, and use them alternately:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct accountNum
{
int sequence;
char sAccountNum[16];
} ACCOUNTNUM;
int main( void )
{
FILE *fp;
ACCOUNTNUM info[2];
int current_index = 0;
bool success = false;
size_t ret;
//open file and verify that it is open
fp = fopen( "acctNumbers.dat", "r" );
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
for (;;) //infinite loop, equivalent to while(1)
{
//read record from file
ret = fread( &info[current_index], sizeof *info, 1, fp);
//swap buffer index
current_index = current_index == 0 ? 1 : 0;
//restart loop if successful
if ( ret == 1 )
{
//set variable to indicate that we have found at
//least one record
success = true;
continue;
}
//break out of loop
break;
}
//print last record, if it exists
if ( success )
printf( "Acc No: %s\n", info[current_index].sAccountNum );
else
printf( "No records found.\n" );
//cleanup
fclose( fp );
}
Or you could use a single buffer and change the way you are calling the function fread, by swapping the second and third function arguments, so that you can detect whether a partial read occurred. If it does occur, you can print an error message and terminate the program.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct accountNum
{
int sequence;
char sAccountNum[16];
} ACCOUNTNUM;
int main( void )
{
FILE *fp;
ACCOUNTNUM info;
bool success = false;
size_t ret;
//open file and verify that it is open
fp = fopen( "acctNumbers.dat", "r" );
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
//read one record per loop iteration
while ( ( ret = fread( &info, 1, sizeof info, fp) ) != 0 )
{
//verify that no partial read occurred
if ( ret != sizeof info )
{
fprintf( stderr, "Error: Partial read detected!\n" );
exit( EXIT_FAILURE );
}
//set variable to indicate that we have found at
//least one record
success = true;
}
//print last record, if it exists
if ( success )
printf( "Acc No: %s\n", info.sAccountNum );
else
printf( "No records found.\n" );
//cleanup
fclose( fp );
}
You are reading records one by one and storing it in info, it stands to reason that the element ACCOUNTNUM stored in info when the cycle ends is exactly the last record in the file, take the following code which is a slight modification of yours, to illustrate the point:
#include <stdio.h>
struct accountNum {
int sequence;
char sAccountNum[16];
};
typedef struct accountNum ACCOUNTNUM;
int LastAccountNumber(ACCOUNTNUM* info) {
int success = 0;
FILE* file = fopen("acctNumbers.dat", "rb");
if (file == NULL) {
return -1; // Allows you to document different types of errors
}
while ((fread(info, sizeof(struct accountNum), 1, file)) == 1) {
success = 1;
}
fclose(file);
return success;
}
int main() {
ACCOUNTNUM N[] = {{123, "2022-1"}, {111, "2022-2"}, {321, "2022-3"}};
FILE* file = fopen("acctNumbers.dat", "wb");
fwrite(N, 1, sizeof N, file);
fclose(file);
ACCOUNTNUM info;
if (LastAccountNumber(&info) == 1) //if -1 couldn't open file, 0 no record read
printf("\nAcc No %s", info.sAccountNum);
}
Will output:
Acc No 2022-3
Which is exactly the last element in the file.
Live sample: https://onlinegdb.com/q760Ow1vQc
Note that you should verify the return value of fopen to confirm the correct access to the file. Closing the file after you are done with it is also advised.
Related
So I am currently trying to write a fairly simple program that takes a file as input, reads it, and lexes it (lexical analysis) (I will get to parsing and interpreting later)
I have written similar programs before that worked perfectly, but surprisingly, this one hangs when I add a function at the end.
The following works perfectly:
#include <stdio.h>
#include <stdlib.h>
#include "include/slex.h"
int main(void) {
// Read the file and save it into source.
printf("start\n");
FILE* fp = fopen("test.sl", "r");
printf("fileopen\n");
fseek(fp, 0, SEEK_END);
printf("seek\n");
char* source = malloc((ftell(fp) + 1) * sizeof (char)); // +1 because of the null terminator.
printf("allocation\n");
fseek(fp, 0, SEEK_SET);
char c;
int i = 0;
while ((c = fgetc(fp)) != EOF) {
source[i++] = c;
} // Iterate through every single character in the file and store it into source.
source[i] = '\0';
fclose(fp);
// Now lex the file;
printf("Lex\n");
lexer_t lexer;
printf("lex2\n");
lexer_init(&lexer, source);
printf("lex3\n");
/*
lex(&lexer);
printf("lex4");
tl_print(&lexer.tokens);
*/
}
But when I uncomment (hope that is an actual word) lex(&lexer), it just hangs. It does not print the previous statements.
The function lex is defined in slex.c and slex.c includes slex.h.
I compiled it with gcc -Wall Wextra -o sl main.c slex.c, and it does not give me any warning, nor any error.
void lex(lexer_t* lexer):
void lex(lexer_t* lexer) {
printf("lex"); // Debugging
// Some people would call this function "make_tokens", I will call it "lex".
while (lexer->current != '\0') {
if (lexer->current == '\n') {
token_t token = {.type = NEWLINE};
tl_append(&lexer->tokens, token);
}
if (isdigit(lexer->current)) {
token_t token = {.type = INT, .int_value = lex_integer(lexer)};
tl_append(&lexer->tokens, token);
}
else if (isalpha(lexer->current)) {
token_t token = {.type = ID, .id_value = lex_identifier(lexer)};
tl_append(&lexer->tokens, token);
}
}
}
I hope someone finds a solution to my problem, because I do not understand it.
Have a nice day and thank you.
And do not hesitate to ask if you need more information, just comment it and I will edit my question.
As Bill Lynch said, Adding fflush(stdout); before lex(&lexer); solved my issue. Thanks to everyone who came by this question, really appreciate your help. I wish you all a nice day.
Credit #Bill Lynch (first comment) who noted "app will [likely] hang" with infinite loop.
It's one thing to printf() to confirm operation. It's something else to check return codes and exit with helpful message.
Below is a rewritten (untested) version of your code using short name aliases to enhance clarity of what is being performed at different locations.
#include <stdio.h>
#include <stdlib.h>
#include "include/slex.h"
void lex( lexer_t *l ) {
fprintf( stderr, "Entered lex()\n" ); // debug
char c;
while( ( c = l->current ) != '\0' ) {
token_t t = { 0 };
if( c == '\n') t.type = NEWLINE;
// still infinite loop...
// what "advances" the pointer?? Credit #Craig Estey
else if( isdigit( c ) ) t.type = INT, t.int_value = lex_integer( l );
else if( isalpha( c) ) t.type = ID, t.id_value = lex_identifier( l );
else { // equivalent to "default:" in a "switch"
fprintf( stderr, "Un-lex-able char ('%c') encountered\n", c );
exit( EXIT_FAILURE );
}
tl_append( &l->tokens, t );
}
}
int main() {
char *fname = "test.sl";
FILE* fp = fopen( fname, "r" );
if( fp == NULL ) {
fprintf( stderr, "Failed to open %s\n", fname );
exit( EXIT_FAILURE );
}
fseek( fp, 0, SEEK_END );
size_t size = ftell( fp );
fseek(fp, 0, SEEK_SET);
fprintf( stderr, "Size %zu\n", size ); // debug
char *buf = malloc( (size + 1) * sizeof *buf ); // +1 because of the null terminator.
if( buf == NULL ) {
fprintf( stderr, "Failed to alloc block %zu\n", size + 1 );
exit( EXIT_FAILURE );
}
size_t nread = fread( buf, sizeof buf[0], size, fp );
if( nread != size ) {
fprintf( stderr, "Expected %zu. Read %zu\n", size. nread );
exit( EXIT_FAILURE );
}
fclose( fp );
buf[ nread ] = '\0';
lexer_t lexer;
lexer_init( &lexer, buf );
lex( &lexer );
// free( buf ); // Unsure if you refer back to original..
tl_print( &lexer.tokens );
return 0;
}
You can add as many optimistic "making progress" printf calls as you'd like. It's the pessimistic "not working" print statements that are needed to push forward.
There is a function load that reads an array of structures from a binary file and returns this array.It works correctly.But the problem is that I can not assign an array of structures the value that the load function returns.
struct tickets
{
char plane [7];
char zona [13];
int rate;
int cost;
};
struct tickets* load(char * filename)
{
FILE * file;
char *symbol;
int m = sizeof(int);
int n, i;
int *pti = (int *)malloc(m);
if ((file = fopen(filename, "r")) == NULL)
{
perror("Error occured while opening file");
}
symbol = (char *)pti;
while (m>0)
{
i = getc(file);
if (i == EOF) break;
*symbol = i;
symbol++;
m--;
}
n = *pti;
struct tickets* ptr = (struct tickets *) malloc(n * sizeof(struct tickets));
symbol = (char *)ptr;
while ((i= getc(file))!=EOF)
{
*symbol = i;
symbol++;
}
fclose(file);
return ptr;
}
int main(void)
{
char * filename = "p.dat";
struct tickets* ticket = NULL;
ticket = load(filename);
}
the following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
informs the main() function as to how many instances of struct tickets were read
informs the user when any error occurs
cleans up after itself
eliminates one of the calls to malloc()
uses the function: fread() to avoid problems with little/big Endian
documents why each header file is included
assumes the ticket count (first 4 bytes of file) is in binary
incorporates appropriate horizontal and vertical spacing for readability
caveat: since no example input file posted, this code is not tested
and now, the proposed code:
#include <stdio.h> // perror(), NULL, FILE*, EOF,
// fopen(), fclose(), fread()
#include <stdlib.h> // malloc(), free(), exit(), EXIT_FAILURE
#define MAX_PLANE_LEN 7
#define MAX_ZONA_LEN 13
#define COUNT_LEN 4
struct tickets
{
char plane [ MAX_PLANE_LEN ];
char zona [ MAX_ZONA_LEN ];
int rate;
int cost;
};
struct tickets* load( char * filename, size_t * ticketCount )
{
FILE * fp;
if ( ! (fp = fopen( filename, "r" )) )
{
perror( "Error occured while opening file" );
exit( EXIT_FAILURE );
}
size_t bytesRead = fread( ticketCount, 1, COUNT_LEN, fp );
if( bytesRead != COUNT_LEN )
{
fprintf( stderr, "failed to read count of number of instances of 'struct tickets'\n" );
fclose( fp );
exit( EXIT_FAILURE );
}
struct tickets* ptr = malloc( *ticketCount * sizeof( struct tickets ) );
if( ! ptr )
{
perror( "malloc failed" );
fclose( fp );
exit( EXIT_FAILURE );
}
for( size_t i = 0; i < *ticketCount; i++ )
{
size_t numBytes = fread( &(ptr[i]), 1, sizeof( struct tickets ), fp );
if( numBytes != sizeof( struct tickets ) )
{
perror( "read of a struct ticket failed" );
fclose( fp );
free( ptr );
exit( EXIT_FAILURE );
}
}
fclose( fp );
return ptr;
}
int main( void )
{
char * filename = "p.dat";
size_t ticketCount = 0;
struct tickets* ticket = load( filename, &ticketCount );
// ...
free( ticket );
}
I am attempting to get my program to read strings from another file, parse them for certain keywords, and then add to a counting variable whenever they appear in the other file. However, I can't seem to get anything but the number of lines to count. What could I be doing wrong here?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// check that fp is not null omitted but needed
const char getLine[1] = "";
const char getFor[4] = "for";
char line[500];
int lineCount = 0;
int forCount = 0;
int x = 0;
int main() {
FILE* fp = fopen("file.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return(-1);
}
while (fgets(line, 499, fp) != NULL) {
strstr(line, getLine);
lineCount++; //Essentially counting the number of lines in file.
}
printf("Line count is %d.\n", lineCount);
memset(line, 0, sizeof(line)); //Resetting the memory of line.
while (fgets(line, 499, fp) != NULL) {
char *findFor;
findFor = strstr(line, getFor);
if (findFor != NULL) { //Attempting to count each time an instant of 'for' appears.
forCount++;
}
}
printf("For count is %d.\n", forCount);
fclose(fp);
}
The code is reading through the whole file to count the lines, but then trying to read through it again (without a rewind() / fseek()). So on the second loop the file is at end-of-file.
It's not necessary to count the lines, and the "for"s in two separate loops, just do it in one.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// check that fp is not null omitted but needed
const char getFor[4] = "for";
char line[500];
int lineCount = 0;
int forCount = 0;
int main( )
{
FILE *fp = fopen( "file.txt", "r" );
if ( fp == NULL )
{
perror( "Error opening file" );
return ( -1 );
}
while ( fgets( line, sizeof( line ), fp ) != NULL )
{
char *findFor;
findFor = strstr( line, getFor );
if ( findFor != NULL )
{
// Attempting to count each time an instance of 'for' appears.
forCount++;
}
lineCount++;
}
printf( "Line count is %d.\n", lineCount );
printf( "For count is %d.\n", forCount );
fclose( fp );
return 0;
}
Also you're not counting the number of "for"s in the file, you're counting the number of lines with "for" in them. If a line has multiples, it's just counted as one.
I have a problem when I try to open a .dat file in a c code. Probably the error I made will be very obvious to some of you but I can't find it so I ask for your help.
The point of my code is to open the .dat file which contains 12.000.000 double numbers and store it in a cuDoubleComplex array which size will be 6.000.000 variables (the even variables of the .dat represents the real part and the odd variables the imaginary (I mean the order, not the value of the variable))
Here is an extract of my code:
#include "cuComplex.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main(void)
{
int n = 12000000; //12.000.000
int lse = n / 2;
double *data = (double *)malloc(12000000 * sizeof(double));
double *data2 = (double *)malloc(12000000 * sizeof(double));
FILE *f, *f2;
cuDoubleComplex *se, *se2;
f = fopen("ref_PRI20.dat", "r");
fread(data, sizeof(double), n, f);
fclose(f);
f2 = fopen("surv_PRI20.dat", "r");
fread(data2, sizeof(double), n, f2);
fclose(f2);
for (int a = 0; a < n; a++)
{
printf("%f\n", data2[a]);
}
se = (cuDoubleComplex*)malloc(lse * sizeof(cuDoubleComplex));
se2 = (cuDoubleComplex*)malloc(lse * sizeof(cuDoubleComplex));
for (int a = 0; a<n / 2; a++)
{
se[a].x = data[2 * a];
se[a].y = data[2 * a + 1];
se2[a].x = data2[2 * a];
se2[a].y = data2[2 * a + 1];
}
free(data);
free(data2);
}
I have added the printf lines and bucle just to check and all I get is "0,0000" as a value. Although when I continue with the programm the se and se2 arrays seems to get random values.
I know the size of the arrays is huge but I need to work with that amount of data.
I also have tried to work in matlab and I had no mistakes in there. This is the code that I used in matlab and everything was fine so I guess there is no problem with the .dat files but only with my code.
f = fopen ("ref_PRI20.dat", 'r');
Data = fread (f, 12e6, 'double');
fclose (f);
dat=(Data(1:2:end)+1i*Data(2:2:end)).';
Here's a scan of the input data so you can see the format. I hope it will help.
enter image description here
Any help will be appreciated.
No error checking on the original code for malloc or fopen.
Since a comment said the file was opened in a text reader and had commas between values, fscanf should be used to input the values instead of fread.
Since the values must be scanned one at a time there isn't a need for the data or data2 pointers.
Could not compile this as I don't have cuComplex.h
#include "cuComplex.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main(void)
{
int n = 12000000; //12.000.000
int lse = n / 2;
FILE *f, *f2;
cuDoubleComplex *se, *se2;
f = fopen("ref_PRI20.dat", "r");
f2 = fopen("surv_PRI20.dat", "r");
se = malloc(lse * sizeof(cuDoubleComplex));
se2 = malloc(lse * sizeof(cuDoubleComplex));
for (int a = 0; a < lse; a++)
{
if ( 1 != fscanf ( f, "%lf ,", &se[a * 2].x])) {
fprintf ( stderr, "could not scan double\n");
break;
}
if ( 1 != fscanf ( f, "%lf ,", &se[a * 2 + 1].y)) {
fprintf ( stderr, "could not scan double\n");
break;
}
if ( 1 != fscanf ( f2, "%lf ,", &se2[a * 2].x)) {
fprintf ( stderr, "could not scan double\n");
break;
}
if ( 1 != fscanf ( f2, "%lf ,", &se2[a * 2 + 1].y)) {
fprintf ( stderr, "could not scan double\n");
break;
}
}
fclose(f);
fclose(f2);
free ( se);
free ( se2);
return 0;
}
the following proposed code:
cleanly compiles
properly checks for and handles errors
reads the data directly into the struct.
Did not use the cuComplex.h file as that was not available.
avoided the use of 'magic' numbers
although the 'b' is not necessary in linux/unix, properly set the open mode to BINARY READ
documented why each header file is included.
eliminated all unnecessary variables and unnecessary calls to malloc()
eliminated all unnecessary data copying.
It may be desirable to read the data in a loop, using a 'moving window' until a 12000000 doubles are read. I'll let you add that feature, if necessary.
And now the proposed code:
#include <stdio.h> // fopen(), fread(), printf(), fprintf(), perror()
#include <stdlib.h> // exit(), EXIT_FAILURE, malloc(), free()
#define N 12000000
struct cuDoubleComplex
{
double x;
double y;
};
int main(void)
{
struct cuDoubleComplex *data = malloc( (N>>1) * sizeof(double));
if( !data )
{
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
struct cuDoubleComplex *data2 = malloc( (N>>1) * sizeof(double));
if( !data2 )
{
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
FILE *f = fopen("ref_PRI20.dat", "rb");
if( !f )
{
perror( "fopen failed" );
free( data );
free( data2 );
exit( EXIT_FAILURE );
}
size_t nmemb = fread(data, sizeof(double), N, f);
if( nmemb != N )
{
fprintf( stderr, "read of first file only read %lu doubles\n", nmemb );
free( data );
free( data2 );
fclose( f );
exit( EXIT_FAILURE );
}
fclose(f);
FILE *f2 = fopen("surv_PRI20.dat", "rb");
if( !f2 )
{
perror( "fopen failed" );
free( data );
free( data2 );
exit( EXIT_FAILURE );
}
size_t nmemb2 = fread(data2, sizeof(double), N, f2);
if( nmemb2 != N )
{
fprintf( stderr, "read of second file only read %lu doubles\n", nmemb2 );
free( data );
free( data2 );
fclose( f2 );
exit( EXIT_FAILURE );
}
fclose(f2);
for (int a = 0; a < N; a++)
{
printf("%f\n", data2[a].y);
}
free(data);
free(data2);
} // end function: main
I'm trying to write a code in C to read binary files from the command line using linux. The specifications on reading the file are as follows:
*The name of the input file is to be passed into the program as a command line argument.
*The program will open this binary file and read the first integer in the file. It will then dynamically create an array of floats of this size using the malloc function.
*The program will then read the floating point values and store them into this newly crated array.
The only thing I've been able to do successfully is open the file.I previously tried to allocate a block of memory by doing
file=double*malloc(30*sizeof(double));
But I kept getting errors and ran into some serious problems when trying to put file in the *ptr parameter of the fread fucntion
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
This is what I have so far:
#include <stdio.h>
int main ( int argc, char *argv[] )
{
if ( argc != 2 )
{
printf( "usage: %s filename", argv[0] );
}
else
{
FILE *file = fopen( argv[1], "r" );
if ( file == 0 )
{
printf( "Could not open file\n" );
}
else
{
int x;
while ( ( x = fgetc( file ) ) != EOF )
{
printf( "%c", x );
}
fclose( file );
}
}
}
Maybe something like this?
int size;
float *floatArray;
FILE *file = fopen( argv[1], "r" );
fread(&size, sizeof(int), 1, file);
floatArray = (float*) malloc(sizeof(float) * size);
fread(floatArray, sizeof(float), size, file);
for(int i = 0; i < size; i++)
{
printf("%d: %f\n", i+1, floatArray[i]);
}
#include <stdio.h>
#include <stdlib.h> /* for malloc() and exit() function */
int main(int argc, char* argv[])
{
FILE *file;
int i, n; /* the counter and num of floating point numbers */
float* val; /* pointer for storing the floating point values */
if(argc<2)
{
printf( "usage: %s filename", argv[0] );
exit(-1);
}
if((file = fopen( argv[1], "rb" )) ==NULL)
{
printf( "Could not open file.\n" );
exit(-2);
}
if(fread(&n, sizeof(int), 1, file)!=1)
{
printf( "Could not read data from file.\n" );
exit(-3);
};
if((val = malloc(n*sizeof(float)))==NULL)
{
printf( "Could not allocate memory for data.\n" );
exit(-4);
};
printf("Number of data values: %d\n", n);
if(fread(val, sizeof(float), n, file)!=n) /* read each floating point value one by one */
{
printf( "Could not read data from file.\n" );
exit(-5);
}
for(i=0; i<n; ++i)
{
printf("%f \t", val[i]); /* now print it */
}
free(val); /* free the allocated memory */
fclose(file); /* close the file */
return 0;
}
NB:
It is assumed that the size of int is same as in the data file and is not short, long or anything else.
Also, the endianness of the data file is assumed to be same as that of the machine on which this above code is run.