Simple C program to read a file line by line
In C you have character oriented input functions (e.g. getchar
, fgetc
), you have formatted input functions (e.g. the scanf
family) and then you have line oriented input functions. (e.g. fgets
and POSIX getline
). When you are reading lines of data, line oriented input functions are the proper tool for the job. (taking user input with scanf
has many pitfalls that new (and even not so new) C programmers fall into)
All line oriented functions read and include the '\n'
in the buffer they fill. You can, and should, remove the newline from the resulting buffer if it will be used later on in your code. A simple
size_t n = strlen (buf);
if (buf[n-1] == '\n')
buf[--n] = 0;
is all you need to overwrite the trailing '\n'
with a nul-terminating character. If you are just printing the line immediately and not storing it for later use, then it's not worth removing the newline (just account for it in your output format string).
Putting those pieces together, you can read each line, handle the first by simply outputting it, and for each remaining line, parse the time (presumable some elapsed time) from the full string read by fgets
with sscanf
and format the output as you specify. E.g.
#include <stdio.h>
#define MAXC 64 /* define constants, don't use magic number in code */
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* buffer to hold each line -- size as reqd */
int line = 1;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, sizeof buf, fp)) { /* read each line in file */
char et[MAXC] = ""; /* buffer for holding time */
if (line == 1) /* if 1st line, just print */
printf ("%d : %s", line, buf); /* note: \n included by fgets */
else {
if (sscanf (buf, "%s", et) != 1) { /* parse up to first whitespace */
fprintf (stderr, "error: invalid conversion, line %d\n", line);
return 1;
}
printf ("%d : %s\n", line, et); /* output elapsed time only */
}
line++; /* increment line count */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
note: you should protect against buffer overrun on parse by including a field-width specifier in the sscanf
format string (e.g. sscanf (buf, "%63s", et)
, and that is one place that all you can do is include magic numbers in your code because there is no way to directly specify a variable width specifier for sscanf
-- unless you creatively use sprintf
to create the format string ahead of time -- but that's for another day..
Example Input File
$ cat dat/et.txt
My test file
00:19.1 123456
00:35.4 testing whitespace end
Example Use/Output
$ ./bin/et <dat/et.txt
1 : My test file
2 : 00:19.1
3 : 00:35.4
Look things over and let me know if you have any further questions.
(note: I take the filename as the first argument to the program, or read from stdin
if no filename is given. C provides for command line arguments -- use them. It's fine to prompt for input if needed, otherwise, its far easier just to specify arguments on the command line :)
Collin
Updated on June 26, 2022Comments
-
Collin almost 2 years
What I would like to do is read the whole first line of the file but then after the first line only read the following lines until whitespace is hit. My end goal is to ask the user what line they want to edit by adding/subtracting time to said line.
Sample File
My test file 00:19.1 123456 00:35.4 testing whitespace end
Desired Output
1: My test file 2: 00:19.1 3: 00:35.4
Code:
#include <stdio.h> #include <stdlib.h> int main() { FILE *fptr1, *fptr2; char filechar[40]; char c[50]; int line_number = 1; int replace_line, temp = 1; printf("Please enter a file name: "); scanf("%s", &filechar); if ((fptr1 = fopen(filechar, "r")) == NULL) { printf("Error locating desired file"); exit(1); } c = getc(fptr1); while (c != EOF) { //printf("%d: %c",line_number, c); printf("%s",c); c = getc(fptr1); //line_number++; } return 0; }
-
Collin almost 7 yearsI get two errors while using this method. The first one being undefined reference to getline, second being ld retured 1 exit status.
-
Collin almost 7 yearsThank you this works to an extent, can you explain the invalid conversion line you added in? Four lines into my test file I errored out.
Line 1: My test file Line 2: 00:19.1 009-021 Line 3: 00:35.4 017-003 018-003 Line 4: 00:35.5 017-000 018-000
-
David C. Rankin almost 7 yearsSure, it is just a validation that one conversion to
%s
was preformed bysscanf
. (essentially I'm just checking thereturn
ofsscanf
) If for whatever reasonsscanf
failed (which with the%s
is rather unlikely), you don't want to keep processing withet
as that would be Undefined Behavior. C doesn't provide any safety nets, so it's up to you to validate all input, conversions, etc...:)
Recall, allscanf
functionsreturn
the number of successful conversion performed. -
Collin almost 7 yearsOkay I understand now. Thank you for all the help!
-
David C. Rankin almost 7 yearsGlad I could help. C is a fantastically flexible language that provides you total control over the machine at a low-level. What's the saying ("With great power comes great responsibility...") Nothing could be truer with C. Validate, validate, validate...
:)
-
Collin almost 7 yearsIs there a way to write over the line I am reading in the while loop while leaving the characters after the whitespace untouched?
-
David C. Rankin almost 7 yearsYes, you can open the file in
"r+"
mode and manipulate the file position indicator to backupstrlen(buf)
chars andfputc
(orfprintf
) some mask char string like'#'
(or whatever), but you must be very very careful that you do not change the byte-count per-line, otherwise you corrupt the file. Seeman fopen
regarding the ANSI C requirement that "a file positioning function intervene between output and input". (in fact, it is always a good idea to consult theman page
any time you use a function you are not completely familiar with:)