fgets and sscanf - c

This code is supposed to get integers from a file which is finput and sort it and gets the first integer in the file which is the number of integers to be sorted and the integers that follow are the integers to be sorted. I don't get how fgets and sscanf work together. Can someone explain how fgets and sscanf work in this code?
FILE *finput;
int *array_int, c1, no_elem;
char numlines[500];
fgets(numlines, 500, finput);
array_int = (int *)malloc(sizeof(int)*no_elem);
if ((sscanf(numlines, "%d", &no_elem) == 1) && array_int!= NULL)
{
for(c1=0; fgets(numlines, 500, finput) != NULL; )
{
if (sscanf(numlines, "%d", &array_int[c1])==1)
{
++c1;
}
}
}

fgets gets a string (i.e. a line of text) from the file.
sscanf parses a string based on the format string. It is reverse to sprintf. the <x>printf and matching <x>scanf functions allow formatted output and input accordingly, with a standard format string. For example, "%d" means "signed integer value", and in context of <x>scanf it means "read it into the next parameter in the following list of parameters" (your array member in your case).
You can parse directly from the file using fscanf, but using fgets + sscanf instead allows for more flexibility and might be safer.

Related

Reading Strings/words and integers from input file

I want to write a little program to read lines from a given .csv/.txt file and print out specific details based on user input.
I'm currently working with a
FILE *input = fopen("Example.csv", "r");
and the input looks like this:
Test000, 40, 0, empty
Test001, 0, -41, empty
Now if I try to to fscanf() from input, it only sets the first char[] and ignores the other variables.
My fscanf() call looks like this:
fscanf(input, "%s , %d , %d , %s", name, &timeA, &timeB, info);
# I'm calling fscanf(...) inside of while()-condition.
# while (fscanf(...) == 4) { *apply logic here* }
So, with this code, fscanf() only ever sets name to 'Test000,', then '40', '0', 'empty' etc., but ignores timeA, timeB, and info.
They are defined as:
char name[51];
int timeA = 0;
int timeB = 0;
char info[51];
I really don't know how to circumvent this problem. Any kind of help will be appreciated!
Thank you for your time.
A scanset could be used. %50[^,] will read up to 50 characters or to a comma.
fscanf(input, " %50[^,], %d , %d , %50s", name, &timeA, &timeB, info);
Note the space before &50[^,] to consume leading whitespace.
Check the return of fscanf. In this case 4 will be returned if all four items are successfully scanned.
fscanf() treats consecutive characters until it encounters white-space as part of a single string (char[]) - so the best option for you would be to remove the commas in your .txt file, and make your fscanf the following: fscanf(input, "%s %d %d %s", name, &timeA, &timeB, info); - your data should look like: Test000 40 0 empty. That's the most straightforward way of making it work.
If you want it to work with your current data format, fscanf() may not be the best option. You would be better off using some functions form <string.h>.
char data[512];
fgets(data, sizeof (data), input);
strcpy(name, strtok(data), ","));
timea = (int) strtol(strtok(data, ","), NULL, 10);
timea = (int) strtol(strtok(data, ","), NULL, 10);
strcpy(info, strtok(data, ","));
(strcpy and strtok are both avaible in <string.h>, strtol() is available in <stdlib.h>)
strcpy is used to copy "strings".
strtok splits a string (Note that it modifies the string it is passed!).
strtol converts a string to an long (which we cast to int).
There are more secure versions of some of the functions available (i.e. strtok_r() and strtol() also comes in an int version (so you don't need to cast its return value to int) called strtod()
If you are on a *nix system, it would be a good idea to run man function_name() (e.g. man strtok) to get a better idea of the function prototype and what it does/how it behaves etc. - or you can always read the man pages online, for example the FreeBSD Online Manual Pages where you can search for the function name and read the relevent man page.

Trying to read formatted file in C

i'm trying to read a formatted file in C this is a sample line of the file:
SURNAME;NAME;MINUTES'SECONDS''ONE-HUNDREDTHS OF A SECOND
I wrote this code:
while(!feof(fp))
{
fscanf(fp,"%[^;]s",surname);
fscanf(fp,"%c",&c);
fscanf(fp,"%[^;]s",name);
fscanf(fp,"%c",&c);
fscanf(fp,"%d'%d''%d",&min,&sec,&sec_cent);
fscanf(fp,"\n");
}
It works well with the name and surname strings, but it doesn't extract the time MINUTES'SECONDS''ONE-HUNDREDTHS OF A SECOND and i don't know why
Can someone help me?
There are a couple of things you may want to change in your code:
Always check the return value of scanf (or fscanf) to see if all the data were read.
Don't use feof() to control a loop.
You don't need to extract the '\n' with scanf, unless you are going to use getline() afterwards.
You don't need the s too, with that format specifier, but you should prevent buffer overflows limiting the number of chars read for each field.
You can use the modifier %*c to skip a char (the ;).
This should be fine:
int min = 0, sec = 0, sec_cent = 0;
char name[128], surname[128];
while ( fscanf(fp, "%127[^;]%*c%127[^;]%*c%2d'%2d''%2d", surname, name,
&min, &sec, &sec_cent) == 5 ) {
// do something...
}

How to save every line in file (IN C) in a variable? :)

I need to save every line of text file in c in a variable.
Here's my code
int main()
{
char firstname[100];
char lastname[100];
char string_0[256];
char string[256] = "Vanilla Twilight";
char string2[256];
FILE *file;
file = fopen("record.txt","r");
while(fgets(string_0,256,file) != NULL)
{
fgets(string2, 256, file);
printf("%s\n", string2);
if(strcmp(string, string2)==0)
printf("A match has been found");
}
fclose(file);
return 0;
}
Some lines are stored in the variable and printed on the cmd but some are skipped.
What should I do? When I tried sscanf(), all lines were complete but only the first word of each line is printed. I also tried ffscanf() but isn't working too. In fgets(), words per line are complete, but as I've said, some lines are skipped (even the first line).
I'm just a beginner in programming, so I really need help. :(
You're skipping over the check every odd number of lines, as you have two successive fgets() calls and only one strcmp(). Reduce your code to
while(fgets(string_0,256,file) != NULL)
{
if( ! strcmp(string_0, string2) )
printf("A match has been found\n");
}
FWIW, fgets() reads and stores the trailing newline, which can cause problem is string comparison, you need to take care of that, too.
As a note, you should always check the return value of fopen() for success before using the returned pointer.

C: simultaneous reading from and writing to file

What i would like to do:
Read bits from one file (input file), and write these (with some probability) inverted bits to other file (output file).
What is the problem:
Probability idea seem not to be working properly. And more importantly, output file always contains more characters then the original input file, while they should contain equal number of characters.
In this code sample, instead of inverted bits i have put 'x' and 'y', so that it is more obvious that output file contains more characters
INPUT file: 01001
OUTPUT file: xyxxxyx
The code:
void invert_bits(FILE **input, FILE **output, double prob){
srand(clock());
char symbol;
while((symbol = getc(*input)) != EOF){
double result = rand()/RAND_MAX;
if(result < prob){
if(simbol == '0'){
char bit = 'x';
fprintf(*output, &bit);
}
else{
char bit = 'y';
fprintf(*output, &bit);
}
}else{
fprintf(*output, &symbol);
}
}
}
(f)printf expects a format string as its second argument. You are providing it with the address of a char, which is not even a valid string (since it is not NUL-terminated).
Don't do that. It's a bad habit. When you use printf, fprintf or sprintf always use a format string. (Read this for more information.)
You could have used fprintf(*output, "%c", bit); but it would be a lot simpler to just print the character with fputc(bit, *output);
I don't understand why you feel the need to pass the FILE* arguments as pointers, by the way.
You aren't using the fprintf function properly.
The function's signature is:
int fprintf ( FILE * stream, const char * format, ... );
Instead of a null terminated string, you're providing it with an address of a char, which might follow by a null character, or might not.
The correct way of printing a character with the *printf functions is:
fprintf(*output, "%c", bit);
P.S. Why are you receiving a pointer to the file handle, i.e. FILE** and not just FILE*?

Reading values from CSV file into variables

I am trying to write a simple piece of code to read values from a CSV file with a max of 100 entries into an array of structs.
Example of a line of the CSV file:
1,Mr,James,Quigley,Director,200000,0
I use the following code to read in the values, but when I print out the values they are incorrect
for(i = 0; i < 3; i++) /*just assuming number of entries here to demonstrate problem*/
{
fscanf(f, "%d,%s,%s,%s,%s,%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
}
Then when I print out the first name, the values are all assigned to the first name:
for(j = 0; j < 3; j++) /* test by printing values*/
{
printf("Employee name is %s\n", inArray[j].firstName);
}
Gives ames,Quigley,Director,200000,0 and so on in that way. I am sure it's how i format the fscanf line but I can't get it to work.
Here is my struct I'm reading into:
typedef struct Employee
{
int ID;
char salutation[4];
char firstName[21];
char surName[31];
char position[16];
int sal;
int deleted;
} Employee;
This is because a string %s can contain the comma, so it gets scanned into the first string. There's no "look-ahead" in the scanf() formatting specifier, the fact that the %s is followed by a comma in the format specification string means nothing.
Use character groups (search the manual for [).
const int got = fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID,
inArray[i].salutation, inArray[i].firstName,
inArray[i].surName, inArray[i].position, &inArray[i].sal,
&inArray[i].deleted);
And learn to check the return value, since I/O calls can fail! Don't depend on the data being valid unless got is 7.
To make your program read the entire file (multiple records, i.e. lines), I would recommend loading entire lines into a (large) fixed-size buffer with fgets(), then using sscanf() on that buffer to parse out the column values. That is much easier and will ensure that you really do scan separate lines, calling fscanf() in a loop will not, since to fscanf() a linefeed is just whitespace.
Might as well post my comment as an answer:
%s reads a full word by default.
It finds the %d, the integer part, then the ,, and then it has to read a string. , is considered valid in a word (it is not a whitespace), so it reads until the end of the line (there is no whitespace until then), not until the first comma... And the rest remains empty. (From this answer)
You have to change the separator with specifying a regex:
fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
Instead of %s, use %[^,], which means "grab all chars, and stop when found a ,".
EDIT
%[^,]s is bad, it would need a literal s after the end of the scanset... Thanks #MichaelPotter
(From Changing the scanf() delimiter and Reading values from CSV file into variables )

Resources