in trying to create a function in c which can get to a specific line in a CSV file (similar to getline()). I'm only allowed to use the ANSI 89/99 standard. Here is what i have wrote so far:
char * getline(FILE * fi,size_t line_length,size_t line_number)
{
size_t i;
char * aux_string;
char * p;
if (fi==NULL)
return NULL;
if ((aux_string = malloc(line_length*sizeof(char)))==NULL)
return NULL;
i=0;
while(fgets(aux_string,line_length,fi)!=NULL||feof(fi)!=0)
{
if (i==line_number)
{
if ((p=strchr(aux_string,'\n'))!=NULL)
{
*p='\0';
}
return aux_string;
}
i++;
}
return NULL;
}
This works fairly ok for a single call to getline(). However, if the function is called like in the example below:
puts(getline(file_a,MAX_LENGTH,0));
puts(getline(file_a,MAX_LENGTH,0));
It outputs the line in the file immediately below, as well as the desired line.
How can I avoid this? What could be causing this? Thanks.
FILE* remembers position inside the file where you ended. So the second time you call fread the file has already read the first line, so you start reading from the second line. You need to fseek(fi, 0, SEEK_SET) rewind the file to the beginning.
char * getline(FILE * fi,size_t line_length,size_t line_number)
{
if (fseek(fi, 0, SEEK_SET) != 0) {
// handle error
}
// rest of your function
Notes:
rewind is equivalent to fseek(.., 0, SEEK_SET), but offers no error handling (weii, except ferror that is).
puts(getline(file_a,MAX_LENGTH,0)); leaks memory allocated inside getline. You should char *pnt = getline(...); puts(pnt); free(pnt) always remember to pick out the trash.
The feof(fi)!=0 condition from inside the while loop looks redundant. If the file has no more lines, the fgets will tell you about it.
I would advise to rename the function to some mygetline or such, not to confuse others. The function getline is already a POSIX function that's been around for very long.
Related
I am reading data from an input file and compressing it with bzip library function calls BZ2_bzCompress in C. I can compress the data successfully. But I cannot write all the compressed data to an output file. Only the first compressed line can be written. Am I missing something here.
int main()
{
bz_stream bz;
FILE* f_d;
FILE* f_s;
BZFILE* b;
int bzerror = -10;
unsigned int nbytes_in;
unsigned int nbytes_out;
char buf[3000] = {0};
int result = 0;
char buf_read[500];
char file_name[] = "/path/file_name";
long int save_pos;
f_d = fopen ( "myfile.bz2", "wb+" );
f_s = fopen(file_name, "r");
if ((!f_d) && (!f_s)) {
printf("Cannot open files");
return(-1);
}
bz.opaque = NULL;
bz.bzalloc = NULL;
bz.bzfree = NULL;
result = BZ2_bzCompressInit(&bz, 1, 2, 30);
while (fgets(buf_read, sizeof(buf_read), f_s) != NULL)
{
bz.next_in = buf_read;
bz.avail_in = sizeof(buf_read);
bz.next_out = buf;
bz.avail_out = sizeof(buf);
printf("%s\n", buf_read);
save_pos = ftell(f_d);
fseek(f_d, save_pos, SEEK_SET);
while ((result == BZ_RUN_OK) || (result == 0) || (result == BZ_FINISH_OK))
{
result = BZ2_bzCompress(&bz, (bz.avail_in) ? BZ_RUN : BZ_FINISH);
printf("2 result:%d,in:%d,outhi:%d, outlo:%d \n",result, bz.total_in_lo32, bz.total_out_hi32, bz.total_out_lo32);
fwrite(buf, 1, bz.total_out_lo32, f_d);
}
if (result == BZ_STREAM_END)
{
result = BZ2_bzCompressEnd(&bz);
}
printf("3 result:%d, out:%d\n", result, bz.total_out_lo32);
result = BZ2_bzCompressInit(&bz, 1, 2, 30);
memset(buf, 0, sizeof(buf));
}
fclose(f_d);
fclose(f_s);
return(0);
}
TL;DR: there are multiple problems, but the main one that explains the problem you asked about is likely that you compress each line of the file independently, instead of the whole file as a unit.
According to the docs of BZ2_bzCompressInit, the bz_stream argument should be allocated and initialized before the call. Yours is (automatically) allocated, but not (fully) initialized. It would be clearer and easier to change to
bz_stream bz = { 0 };
and then skip the assignments to bz.opaque, bz.alloc, and bz.free.
You store but do not really check the return value of your BZ2_bzCompressInit call. It does eventually get tested in the condition of the inner while loop, but you do not detect error conditions there, but instead just success and normal completion conditions.
Your handling of the input buffer is significantly flawed.
In the first place, you set the number of available input bytes incorrectly:
bz.avail_in = sizeof(buf_read);
Since you're using fgets() to read data into the buffer, under no circumstances is the full size of the buffer occupied by input data, because fgets() ensures that a string terminator is written into the array. In fact, it could be worse because fgets() will stop at after newlines, so it may provide as few as just one input byte on a successful read.
If you want to stick with fgets() then you need to use strlen() to determine the number of bytes available from each read, but I would suggest that you instead switch to fread(), which will more reliably fill the buffer, indicate with its return value how many bytes were read, and correctly handle inputs containing null bytes.
In the second place, you use BZ2_bzCompress() to compress each buffer of input as if it were a complete file. When you come to the end of the buffer, you finish a compression run and reinitialize the bz_stream. This will definitely interfere with decompressing, and it may explain why your program (seems to) compress only the first line of its input. You should be reading the whole content of the file (in suitably-sized chunks) and feeding all of it to BZ2_bzCompress(... BZ_RUN) before you finish up. There should be one sequence of calls to BZ2_bzCompress(... BZ_FINISH) and finally one call to BZ2_bzCompressEnd() for the whole file, not per line.
You do not perform error detection or handling for any of your calls to standard library or bzip functions. You do handle the expected success-case return values for some of these, but you need to be rpepared for errors, too.
There are some additional oddities
you have unused variables nbytes_in, nbytes_out, bzerror, and b.
you open the input file as a text file, though whether that makes any difference is platform-dependent.
the ftell() / fseek() pair has no overall effect other than setting save_pos, which is not otherwise used.
although it is not harmful, it also is not useful to memset() the output buffer to all-zeroes at the end of each line (or initially).
Given that you're compressing the input, it's odd (but again not harmful) that you provide six times as much output buffer as you do input buffer.
I have seen programs for file handling and in one of the program using fseek as shown below:
/* This example opens a file myfile.dat for reading.
After performing input operations (not shown), it moves the file
pointer to the beginning of the file.
*/
#include <stdio.h>
int main(void)
{
FILE *stream;
int result;
if (stream = fopen("myfile.dat", "r"))
{ /* successful */
if (fseek(stream, 0L, SEEK_SET)); /* moves pointer to */
/* the beginning of the file */
{ /* if not equal to 0
then error ... */
}
else {
/* fseek() successful */
}
}
Like this can one move the file pointer to the next line immediately after that line
BO_ 377 FC_DM_MISC: 8 FC
SG_ DATA3 m11 : 31|8#0+ (1,0) [0|0] "" DM
These are the two lines and I want to program in a way that when one identifies the number 377 the pointer should now go to the next line i.e., to the line SG_ DATA3 inspite of the white spaces after 8 FC. How can one do that using fseek in C?
Try this code . It may help you .Here the Each line of the Input file is converted to string ,since string manipulation is very simple comparing to complex fseek() function.This may not be perfect answer but this will be very simple solution.
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *stream;
int result;
char tmp[100]; // assuming that max length of a line in myfile.dat is 100.
if (stream = fopen("myfile.dat", "r"))
{ /* successful */
fscanf(stream, "%100[^\n]", tmp); // assuming that max length of a line in myfile.dat is 100.
printf("%s", tmp);
if (strstr(tmp, "377"))
{ // check for 337
fscanf(stream, "%100[^\n]", tmp); // next line is in the string tmp .
// continue your program.
//printf("%s", tmp);
}
}
}
fseek is used for binary data, if you work on a text file you should use either fgets or getline(recommended to use getline).
There's an open discussion of "fgets() vs getline" and many say that "fgets is deprecated" is only a gcc propaganda in favor to their specific getline().
A possible flaw in fgets() is that it doesn't tell you anything if there are null bytes being read, something you can get away with getline().
But then again if you don't like gcc, or use something different, use fgets(). If you are stuck with gcc, then use getline().
I wanted to write, read and print to and from the same file. But when the program executes, it can write but it can't read or print the data I have written. When I execute the program, it stops working after writing to the file. I have verified that the file (penny.txt) contains data after the write operation.
I don't know where this is going wrong - how can I read and print the data?
I'm quite new to this, so please take that in mind when answering.
#include<stdio.h>
int main()
{
char ch;
char penny[50],pen[50];
FILE *Object;
Object = fopen("Penny.txt","w+");
fgets(penny, sizeof penny, stdin);
fprintf(Object,penny);
fscanf(Object,"%s",pen);
printf("%s",pen);
return 0;
}
You're at the end of the file when you call fscanf(). Use fseek to return to the beginning:
/* this ignores a whole host of other issues */
fprintf(Object,penny);
/* optional: fflush(Object); */
/* after the call to fprintf you're at the end of the "stream" in this case,
* go back to the beginning:
*/
fseek(Object, 0, SEEK_SET);
/* now we have something to read! */
fscanf(Object,"%s",pen);
printf("%s\n",pen);
You did not notice this problem due to a complete lack of error checking. fopen, fprintf, and fscanf all have error conditions listed, and all use their return value to signal a problem. You ignore these return values at your own peril.
#include<stdio.h>
int main()
{
//char ch;//unused!
char penny[50],pen[50];
FILE *Object;
Object = fopen("Penny.txt","w+");
fgets(penny, sizeof penny, stdin);
fprintf(Object,"%s", penny);//it troubled indicator(%) is included
fflush(Object);//Buffer flush : So that there is no wrote
rewind(Object);//rewind the position of access to the file
fscanf(Object,"%s",pen);
printf("%s",pen);
return 0;
}
You need to Use fseek() to move back the file current position inside the file.
int fseek ( FILE * stream, long int offset, int origin );
Reposition stream position indicator
Sets the position indicator associated with the stream to a new position.
stream
Pointer to a FILE object that identifies the stream.
offset
Binary files: Number of bytes to offset from origin.
Text files: Either zero, or a value returned by ftell.
origin
Position used as reference for the offset. It is specified by one of the following constants defined in exclusively to be used as arguments for this function:
Constant Reference position
SEEK_SET Beginning of file
SEEK_CUR Current position of the file pointer
SEEK_END End of file
*
try this:
#include<stdio.h>
int main()
{
char ch;
char penny[50],pen[50];
FILE *Object;
Object = fopen("Penny.txt","w+");
fgets(penny, sizeof penny, stdin);
fprintf(Object,penny);//now the file is in EOF
fseek(Object,-1*(strlen(penny),SEEK_CUR);//<===move back |penny| in the file
/* optional or:fseek(Object,0,SEEK_SET);<===move to start of file */
fscanf(Object,"%s",pen);
printf("%s",pen);
return 0;
}
I'm trying to print to a text file numerous variables yet it doesn't work.
I checked and verified that i write it in the correct syntax.
I also checked the return value and it's positive therefore i know it did write to the file, however when i open the file it's empty.
I would be happy for some help.
This is the code:
I initiate DynsaleDayPtr in the main:
FILE* DynsaleDayPtr = CreateTextFiles("sale_day.txt");
Create function:
FILE* CreateTextFiles (char* fileName)
{
FILE* saleFilePtr=NULL;
if((saleFilePtr=fopen(fileName,"a+"))==NULL)
printf("File couldn't be opened\n");
return saleFilePtr;
}
The call to the function TextAddSale is done from a function that is called in the main:
TextAddSale(DynSaleDayPtr,dynNumOfRecords);
Bool TextAddSale (FILE* DynsaleDayPtr, int* dynNumOfRecords)
{
char id[6];
char name [50];
char priceChar[20];
char* tmp = NULL;
int price=-1;
DynamicRecord * newRec=NULL;
scanf("%s%s%s",id,name,priceChar);
newRec = (DynamicRecord *)malloc(sizeof(DynamicRecord));
if (newRec == NULL)
return False;
tmp = (char*)malloc(strlen(name)+1);
if (tmp == NULL)
{
free (newRec);
return False;
}
strcpy(tmp,name);
newRec->productName = tmp;
strcpy(newRec->productId, id);
newRec->productPrice=atoi (priceChar);
if (fprintf(DynsaleDayPtr,"%d %s %s %d", strlen(newRec->productName),
newRec->productId, newRec->productName, newRec->productPrice)>0)
{
*dynNumOfRecords=(*dynNumOfRecords)+1;
return True;
}
}
thanks!
You need to flush the stream.
fflush(FILE*);
Of course, you have to close the stream if you have done with it.
fclose(FILE*);
Agree with #pmg - try something like this:
FILE *pFile = fopen("foo.txt","w");
if (pFile==NULL)
bad();
fprintf(pfile,"Hello world\n");
fclose(pfile);
make that work first - then fix whatever's wrong in the big app -
A thought:
scanf("%s%s%s",id,name,priceChar);
the above statement is a bit dodgy since you haven't said how many bytes
should go in each string.
better to use fgets() then parse the string retrieving the individual values
or create a better format specifier.
If the above statement causes a memory overwrite the rest of your program
could malfunction causing things like what you describe.
fprintf() most likely uses buffered output. Therefore, you should either fflush() the DynSaleDayPtr stream or, better yet, print a newline to the file. The latter has the added benefit of making the file contents actually readable...
Also, don't forget to close() the stream when you're finished with writing. This will also render fflush() unnecessary.
i want to open a file, write some data on it so i have to use (Fopen) " i can't use open because i need fopen in some other things "
now if i want to write on the file using fwrite it just don't i don't know why this is what i referred to in my code #option1, but if i get the file descriptor and use normal write method everything works fine see #option 2 below.
anyone can help me to make fwrite works ?
char file_data[256] // has some values
int file_size = strlen(file_data);
FILE *file;
file = fopen(MY_FILE_NAME, "w+");
if(!file){//edited
return false;
}
#option 1//this is not working
fwrite(file_data,1,file_size,file);
#end of option 1
#option 2//this works
int fd = fileno(file);
int x = write(fd,file_data,file_size);//
#end of option 1
EDIT
my file_data is something like this
4 bytes is reserved for an integer (required)
200 bytes is reserved for a string (optional)
buffered IO operations use a buffer that is managed by the C lib. Your "problem" is that fwrite is buffered meaning that in order to write to the file you most likely need to flush it with fflush() or just close the file.
First of all:
if(!file < 0 ){
return false;
}
file is either NULL (on failure) or not (on success) - there's no point in testing it against 0 as it's a pointer (therefore, unsigned).
Your fwrite call seems OK, but you should make sure that the amount you're trying to write is correct (is there a null-terminated string inside file_data?).
Another problem you may be facing is that you don't close or flush the file - this may cause some data to remain in the file-buffer and not be written to the disk.
If you want to check the fopen() return value, do like this:
if (file == NULL) return false;
then, if you want to write a string fputs() is preferable, IMHO, because it communicates better that what you're writing is a string.
Since, according to your last edit, you aren't writing ASCII strings, this is what you should code:
#include <stdio.h>
struct String
{
int size;
char data[200];
};
int main()
{
struct String s;
FILE* file = NULL;
file = fopen("filename", "wb+");
memset(&s, '\0', sizeof(s));
strcpy(s.data, "Hello, world!");
s.size = strlen(s.data);
fwrite(&s, 1, sizeof(s), file);
if (!file) return 1;
fclose(file);
}
At first sight, the mistake seems to be at line #2:
int file_size = strlen(file_data);
This only works if there exists a terminal nul character. So file_size must be either provided for example as a function argument or the you must use the full size of the array.
The following should work:
int write_in_my_file(int data_int, const char* data_str)
{
size_t written;
FILE* file = fopen(MY_FILE_NAME, "wb+"); /* SuperJulietta */
if (!file) return false;
written = fwrite(&data_int, sizeof(data_int), 1, file);
if (written == sizeof(data_int))
{
if (opt_str) fputs(opt_str, file);
}
fclose(file);
return written == sizeof(data_int);
}
Note: this code was not compiled, and error handling is partial.
Edit : if you don't close the file, you'll have to call fflush instead.
You have to put a fflush(file); after the fwrite to force the writing of the data or you can also remove the buffer completely by doing a setbuf(file, NULL); after your fopen call.
I think you need to either do fclose(file) or fflush(file). because fopen is buffered IO so It does not write immidiately, so to ensure that file write is done, you need to do this.
I guess your fwrite code is not the problem.
Whenever the first byte in your file_data is \0 then you write nothing. Since the data is not a string, write 256 bytes. This code works:
#include <stdio.h>
#include <string.h>
#define MY_FILE_NAME "sample.bin"
#define SAMPLE_DATA "Content Content"
int main()
{
char file_data[256];
int file_size = sizeof(file_data);
// fill in some sample data
memcpy(file_data, SAMPLE_DATA, sizeof(SAMPLE_DATA));
FILE *file = fopen(MY_FILE_NAME, "w+");
if (file) {
fwrite(file_data, 1, file_size, file);
fclose(file);
}
}
You see, this is your fwrite. I use sizeof instead of strlen to determine the amount of bytes that will be written...
BR
fwrite is used for binary output, so you have to open file with "wb"