I am trying to get one whole line from some text file instead of one word until it meets white space, here is source code:
#include <stdio.h>
void main() {
int lineNum=0;
char lineContent[100];
scanf("%s", &lineContent);
printf("%s\n", lineContent);
}
And here is my text file, called test.txt, content containing 2 lines:
111 John Smith 100 98 1.2 2.5 3.6
222 Bob Smith 90 91 3.2 6.5 9.6
And I run it with following command:
a.out < test.txt
My output is just:
111
What I want is:
111 John Smith 100 98 1.2 2.5 3.6
Of course, I can simply use while statement and read recursively until it meets EOF, but that is not what I want. I just want to read one whole line per each time I read from file.
How can I do this?
Thank you very much.
fgets() is the most convenient standard library function for reading files one line at a time. GNU getline() is even more convenient, but it is non-standard.
If you want to use scanf(), however, then you can do so by using the [ field descriptor instead of s:
char lineContent[100];
scanf("%99[^\n]", &lineContent);
getchar();
Note the use of an explicit field width to protect against overrunning the bounds of lineContent in the event that a long line is encountered. Note also that the [ descriptor differs from s in that [ does not skip leading whitespace. If preserving leading whitespace is important to you, then scanf with an s field is a non-starter.
The getchar() reads the terminating newline, presuming that there is one, and that scanf() did not read 99 characters without reaching the end of the line.
If you want to read line by line, then use fgets() or getline() (available on POSIX systems) instead of scanf(). scanf() stops at first whitespace (or matching failure) for %s.
Related
I am trying to learn C and have recieved a homework assignment to write code which can read data from a .txt file and print out particular lines.
I wrote the following:
#include <stdio.h>
void main() {
char str[5];
FILE *fp;
fp=fopen("data.txt","r");
int i;
for (i=1;i<=5;i++){
fgets(str,5,fp);
printf("%d \n",i);
if (i==1||i==3||i==5) {
printf("%s \n \n",str);
}
}
}
The file data.txt is just the following:
3.21
5.22
4.67
2.31
2.51
1.11
I had read that each time fgets is run, the pointer is updated to point to the next line. I thought I could keep running fgets and then only print the string str when at the correct value for i (the line I want output on the console).
It partially worked, here is the output:
1
3.21
2
3
5.22
4
5
4.67
Process returned 8 (0x8) execution time : 0.024 s
Press any key to continue.
It did only print when i had the correct values, but for some reason it only printed the first 3 lines, even though fgets was supposed to have been run 5 times by the last iteration, and so the pointer should have been reading the last line.
Can someone explain why the pointer did not update as expected and if there is an easier way to slice or index through a file in c.
You need to account for (at least) two additional characters, in addition to the numbers you have in the file. There is the end-of-line delimiter (\n on UNIX/Mac, or possibly \r\n on Windows... so maybe 3 additional characters), plus (from the fgets documentation):
A terminating null character is automatically appended after the characters copied to str.
A lot of the C functions that manipulate character arrays (ie. strings) will give you this extra null "for free" and it can be tricky if you forget about it.
Also, a better way to loop over the lines might be:
#define MAX_CHARS 7
char buf[MAX_CHARS];
while((fgets(buf, MAX_CHARS, fp)) != NULL) {
printf("%s\n", buf);
}
It's still not the best way to do it (no error checking) but a little more compact/readable and idiomatic C, IMO.
Data file:
Newton 30 United Kingdom Scientist
Maxwell 25 United Kingdom Mathematician
Edison 60 United States Engineer
Code to read it:
#define MAX_NAME 50
#define MAX_COUNTRY 25
#define MAX_PROFILE 20
struct person
{
char *name;
int age;
char *country;
char *profile;
};
struct person pObj;
pObj->name = (char *) malloc(sizeof(MAX_NAME));
pObj->country = (char *) malloc(sizeof(MAX_COUNTRY));
pObj->profile = (char *) malloc(sizeof(MAX_PROFILE));
fscanf(fPtr,"%s\t%d\t%s\t%s\n",pObj->name,&pObj->age,pObj->country,pObj->profile);
I wrote a program to read tab delimited record to a structure using fscanf(). Same thing I can do by strtok(), strsep() functions also. But If I use strtok(), I forced to use atoi() function to load age field. But I don't want to use that atoi() function. So I simply used fscanf() to read age as Integer directly from the FILE stream buffer. It works fine. BUT for some record, country field is empty as like below.
Newton 30 United Kingdom Scientist
Maxwell 25 Mathematician
Edison 60 United States Engineer
When I read the second record, fscanf() doesn't fill empty string to the country field instead it has been filled with profile data. We understand fscanf() works that way. But is it there any option to scan the country field even though it is empty in the file? Can I do this without using atoi() function for age? i.e., reading fields by that respective types but not all the fields as strings.
Original format
The %s conversion specification skips any white space (blanks, tabs, newlines, etc) in the input, and then reads non-white-space up to the next white space character. The \t appearing in the format string causes fscanf() to skip zero or more white space characters (not just tabs).
You have:
fscanf(fPtr,"%s\t%d\t%s\t%s\the n", pObj->name, pObj->age, pObj->country, pObj-profile);
You need to pass a pointer to the age and you need an arrow -> between pObj and profile (please post code that could compile; it doesn't inspire confidence when there are errors like this):
fscanf(fPtr,"%s\t%d\t%s\t%s\the n", pObj->name, &pObj->age, pObj->country, pObj->profile);
Given the first input line:
Newton 30 United Kingdom Scientist
fscanf() will read Newton into pObj->name, 30 into pObj->age,UnitedintopObj->countryandKingdomintopObj->profile.fscanf()` and family are very casual about white space, in general. Most conversions skip leading white space.
After the 4 values are assigned, you have \the n" at the end of the format. The tab skips the white space between Kingdom and Scientist, but the data doesn't match he n, so the scanning stops — not that you're any the wiser for that.
The next operation will pick up where this one stopped, so the next pObj->name will be assigned Scientist and then the pObj->age conversion will fail because Maxwell doesn't represent an integer. The conversions stop there on that fscanf().
And so the problems continue. Your claimed output can't be attained with the code you show in the question.
If you're adamant that you must use fscanf(), you'll need to use scan sets such as %24[^\t] to read the country. But you'd do better using fgets() or POSIX function getline() to read whole lines of input, and then perhaps use sscanf() but more likely use strcspn() or strpbrk() from Standard C (or perhaps strtok() or — far better — POSIX strtok_r() or Windows strtok_s(), or non-standard strsep()) to split the line into fields at tabs. Note that strtok_r() et al don't care how many repeats there are of the delimiter (tabs in your case) between the fields; you can't have empty fields with them. You can identify empty fields with strcspn(), strpbrk() and strsep().
Cleaned up format
The format string has been revised to:
fscanf(fPtr,"%s\t%d\t%s\t%s\n", pObj->name, &pObj->age, pObj->country, pObj->profile);
This won't work, but can now be adapted so it will work.
if (fscanf(fPtr," %49[^\t]\t%d\t%24[^\t]\t%19[^\n]", pObj->name, &pObj->age, pObj->country, pObj->profile) != 4)
…handle a format error…
Beware trailing white space in scanf() format strings. The leading blank skips any newline left over from previous lines, and skips any leading white space on a line. The %49[^\t] looks for up to 49 non-tabs; the tab is optional and matches any sequence of white space, but the first character will be a tab unless the name was too long. Then it reads a number, more optional white space (it doesn't have to be a tab, but it will be unless the data is malformatted), then up to 24 non-tabs, white space again (of which the first character will be a tab unless there's a formatting problem), and up to 19 non-tabs. The next character should be a newline, unless there's a formatting problem.
my question is how can I read specific sections from a file? For instance, if my file was:
454545454 Joe Brown 70 50 40
656565656 David Smith 80 90 100
383838383 George Williams 95 100 80
How could I read the first string (9-Digit #), skip over the name, and then read the 3 sets of numbers?
I think that you could notice that the white space is your sentinel. I'm thinking that maybe you can store the whole file into a char* and asking for this sentinel each time.
Other solution could be using atoi (ascii to int) for validate if it's a number or a letter. You can also read about fread and fseek.
I think that the best way is to mix both solution... find each sentinel and try to parse it using atoi.
The main idea is that you try to find some pattern in the file that allows you to think the algorithm.
In C, most of the times you have to solve the logic by yourself.
Hope it helps!
Instead of "reading specific sections," read file line by line and save the information you want and discard the others. scanf is used to read formatted from an external source into program variables. Since scanf returns the number of successful reads from the source, you can use that to do some error checking.
char num_string[STR_LEN];
int numbers[3];
char dummy1[STR_LEN], dummy2[STR_LEN];
int num_read = scanf( "%s%s%s%d%d%d", num_string, dummy1, dummy2, &numbers[0], &numbers[1], &numbers[2] );
if( num_read != 6 )
// error
else
{
// do stuff with num_string, and numbers[0]-numbers[2]
}
If i have a text file with each line of different length, how does the following code work??
FILE *ptr;
char str[100];
ptr=fopen("hi.txt","r");
while(fgets(str,100,ptr)!=NULL)
{
........
........
}
In this code the 'str' will hold 100 characters which includes some of the characters from 2nd line of text file(if the 1st line of file is 90 chars then 10 chars from second line will also be read)..
If i am correct, can you please tell how to read exactly only one line during every ready?
fgets will read up to a single line OR the value passed in as the second parameter.
fgets man page
As long as none of your lines are longer than 99 characters (saving one for the NUL terminator, your code will work as expected.
If you call fgets on a line that is longer than N-1, your next read will continue where it left off and go another 99 bytes or until it finds the end of the line.
I have a txt file named prob which contains:
6 2 8 3
4 98652
914
143 789
1
527 146
85
1 74 8
7 6 3
Each line has 9 chars and there are 9 lines. Since I cant make a string array in c, im be using a two dimensional array. Careful running the code, infinite loops are common and it prints weird output. Im also curious as to where does it stop taking in the string? until newline?
expected result for each "save": 6 2 8 3
or watever the line contained.
#include <stdio.h>
FILE *prob;
main()
{
prob = fopen("prob.txt", "r");
char grid_values[9][9];
char save[9];
int i;
for (i = 0; (fscanf(prob, "%s", save) != EOF); i++)
{
int n;
for (n = 0; n <= 9; n++)
{
grid_values[i][n] = save[n];
printf("%c", grid_values[i][n]);
}
}
fclose(prob);
}
if you use fscanf, it will stop after a space delimiter..
try fgets to do it.. It will read line by line..
for (i = 0; (fgets(save, sizeof(save), prob) != EOF); i++)
the detail of fgets usage can be found here:
http://www.cplusplus.com/reference/clibrary/cstdio/fgets/
--edited--
here's the second
while(!feof(file))
{
fgets(s, sizeof(s), file); ......
}
I think it'll work well..
This looks like a homework problem, so I will try to give you some good advice.
First, read the description of the fscanf function and the description of the "%s" conversion.
Here is a snip from the description I have for "%s":
Matches a sequence of non-white-space characters; the next pointer must be a pointer to a character array that is long enough to hold the input sequence and the terminating null
character (’\0’), which is added automatically. The input string stops at white space or
at the maximum field width, whichever occurs first.
Here are the two important points:
Each of your input lines contains numbers and whitespace characters. So the function will read a number, reach whitespace, and stop. It will not read 9 characters.
If it did read 9 characters, you do not have enough room in your array to store the 10 bytes required. Note that a "terminating null character" will be added. 9 characters read, plus 1 null, equals 10. This is a common mistake in C programming and it is best to learn now to always account for the terminating null in any C string.
Now, to fix this to read characters into a two dimensional array: You need to use a different function. Look through your list of C stdio functions.
See anything useful sounding?
If you haven't, I will give you a hint: fread. It will read a fixed number of bytes from the input stream. In your case you could tell it to always read 9 bytes.
That would only work if each line is guaranteed to be padded out to 9 characters.
Another function is fgets. Again, carefully read the function documentation. fgets is another function that appends a terminating null. However! In this case, if you tell fgets a size of 9, fgets will only read 8 characters and it will write the terminating null as the 9th character.
But there is even another way! Back to fscanf!
If you look at the other conversion specifiers, you could use "%9c" to read 9 characters. If you use this operation, it will not add a terminating null to the string.
With both fread and fscanf "%9c" if you wanted to use those 9 bytes as a string in other functions such as printf, you would need to make your buffers 10 bytes and after every fread or fscanf function you would need to write save[9] = '\0'.
Always read the documentation carefully. C string functions sometimes do it one way. But not always.