Simple C program to read a file line by line - c

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;
}

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 :)

Please try if this C code can help you. It just reads the file line by line and replaces whitespace with the string termination character \0.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
char* replace_char(char* str, char find, char replace){
char *current_pos = strchr(str,find);
while (current_pos){
*current_pos = replace;
current_pos = strchr(current_pos,find);
}
return str;
}
int main(void)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("/home/developer/CLionProjects/untitled4/download.out", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
int count=0;
while ((read = getline(&line, &len, fp)) != -1) {
if (count==0) printf("%s", line);
else printf("%s\n", replace_char(line, ' ', '\0'));
count++;
}
fclose(fp);
if (line)
free(line);
exit(EXIT_SUCCESS);
}
File
My test file
00:19.1 123456
00:35.4 testing whitespace end
Output
My test file
00:19.1
00:35.4

Related

How can I have my C code read and display multiple lines from a C file?

I am trying my best to have the code read multiple lines of texts from a text file. A sample output of what the text file contains is shown below (the first is studentID, second is gpa, third is initials):
12345 3.50000 aj
34286 4.10000 be
I only need the screen to display to me the studentID and the gpa. I have gotten it to display the first line of code, but not the rest of the code. My code is as follows:
void displayStudentGpas(FILE* inFile){
int studentID;
double gpa;
char pipe = '|';
while(fscanf(inFile, "%d %lf", &studentID, &gpa) == 2){
printf("||%11d%4c%11.2lf%8c| \n", studentID, pipe, gpa, pipe);
printf("||--------------|---------------------|| \n);
}
}
I would appreciate any help you could give me... thank you!
In C, whenever you need to read a line-at-a-time, you use a line-oriented input function like fgets() or POSIX getline(). That way you consume the entire line of input and you do not leave a partial line unread.
In your case you attempt to use a formatted-input function fscanf() with the "%d %lf" format string. (but Good Job! on checking the return!). What happens when you attempt to read:
12345 3.50000 aj
is that 12345 3.50000 is read from the line leaving "aj\n" in the input buffer -- unread. Then on your next iteration your attempt to read again with "%d %lf" and a matching failure occurs because "aj" is not valid integer input.
When a matching failure occurs, character extraction ceases leaving "aj" in the input buffer unread -- again. But since you correctly check the return and condition your read loop on the successful read of both values -- your loop terminates instead of spinning off into an infinite loop attempting to read "aj" over-and-over again.
(note: your third-category of input functions are character-oriented functions such as getchar() or fgetc() or getc())
The solution is simple. Use fgets() and a sufficiently sized buffer (don't skimp on size) to read the entire line and then use sscnaf() to parse all (or only the first two) values from the buffer and output the studentID and gpa. You can do:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXI 8
int main (int argc, char **argv) {
char buf[MAXC]; /* buffer to hold entire line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
int id; /* ID */
double gpa; /* GPA */
if (sscanf (buf, "%d %lf", &id, &gpa) == 2) /* parse id & gpa from buf */
printf ("%-8d %5.1f\n", id, gpa); /* on success -- output */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
(note: the program will read from the filename passed as the first argument to the program, or read from stdin by default if no argument is provided -- the way many Linux utilities do)
If you are on an embedded system with limited memory, you can adjust MAXC accordingly. (say 32 would be a nice power-of-two and still provide a 15-char margin)
Example Use/Output
Simply passing your example input in stdin, you would receive:
$ cat << eof | ./bin/studentidgpa
> 12345 3.50000 aj
> 34286 4.10000 be
> eof
12345 3.5
34286 4.1
To read values from a filename, you would use:
$ ./bin/studentidgpa filetoread
Or if you like, you can redirect filetoread on stdin, e.g.
$ ./bin/studentidgpa < filetoread
Look things over and let me know if you have additional questions.
This would be my solution to your problem in cpp, please let me know if I should convert it to c for you:
#define studentCount 2
//change if you have more than 2 students
void printStudents(std::string FILENAME) {
std::ifstream file(FILENAME);
int studentID[studentCount];
double gpa[studentCount];
std::string name[studentCount];
std::string line; //tmp variable to store a line
if (file.is_open()) {
int i = 0;//Counter to keep track of the current student
while (getline(file, line))
{
int a = 0;//Counter to keep track of the current student data
std::stringstream ss(line);
while (getline(ss, line, ' ')) { //Split line by ' ' and store in line
switch (a)
{
case 0:
studentID[i] = std::stoi(line); //Convert string to int
break;
case 1:
gpa[i] = std::stod(line); //Convert string to double
break;
case 2:
name[i] = line; //Nothing :(
break;
default:
break;
}
a++;
}
i++;
}
}
for (int i = 0; i < studentCount; i++) {
std::cout << "ID: " << studentID[i] << " | gpa:" << gpa[i] << " | Name: " << name[i] << "\n";
std::cout << "||--------------|---------------------|| \n";
}
}
int main()
{
std::string FILENAME = "test.txt";
printStudents(FILENAME);
}

Use fscanf to read strings and empty lines

I have a text file containing keywords and integers and have access to the file stream in order to parse this file.
I am able to parse it by doing
while( fscanf(stream, "%s", word) != -1 ) which gets each word and int in the file for me to parse, but the problem I'm having is that I cannot detect an empty line "\n" which then I need to detect for something. I can see that \n is a character thus not detected by %s. What can I do to modify fscanf to also get EOL characters?
You can do exactly what it is you wish to do with fscanf, but the number of checks and validations required to do it properly, and completely is just painful compared to using a proper line oriented input function like fgets.
With fgets (or POSIX getline) detecting an empty line requires nothing special, or in addition to, reading a normal line. For example, to read a line of text with fgets, you simply provide a buffer of sufficient size and make a single call to read up to and including the '\n' into buf:
while (fgets (buf, BUFSZ, fp)) { /* read each line in file */
To check whether the line was an empty-line, you simply check if the first character in buf is the '\n' char, e.g.
if (*buf == '\n')
/* handle blank line */
or, in the normal course of things, you will be removing the trailing '\n' by obtaining the length and overwriting the '\n' with the nul-terminating character. In which case, you can simply check if length is 0 (after removal), e.g.
size_t len = strlen (buf); /* get buf length */
if (len && buf[len-1] == '\n') /* check last char is '\n' */
buf[--len] = 0; /* overwrite with nul-character */
(note: if the last character was not '\n', you know the line was longer than the buffer and characters in the line remain unread -- and will be read on the next call to fgets, or you have reached the end of the file with a non-POSIX line ending on the last line)
Putting it altogether, an example using fgets identifying empty lines, and providing for printing complete lines even if the line exceeds the buffer length, you could do something like the following:
#include <stdio.h>
#include <string.h>
#define BUFSZ 4096
int main (int argc, char **argv) {
size_t n = 1;
char buf[BUFSZ] = "";
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, BUFSZ, fp)) { /* read each line in file */
size_t len = strlen (buf); /* get buf length */
if (len && buf[len-1] == '\n') /* check last char is '\n' */
buf[--len] = 0; /* overwrite with nul-character */
else { /* line too long or non-POSIX file end, handle as required */
printf ("line[%2zu] : %s\n", n, buf);
continue;
} /* output line (or "empty" if line was empty) */
printf ("line[%2zu] : %s\n", n++, len ? buf : "empty");
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
Example Input File
$ cat ../dat/captnjack2.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Example Use/Output
$ ./bin/fgetsblankln ../dat/captnjack2.txt
line[ 1] : This is a tale
line[ 2] : empty
line[ 3] : Of Captain Jack Sparrow
line[ 4] : empty
line[ 5] : A Pirate So Brave
line[ 6] : empty
line[ 7] : On the Seven Seas.
So Why Does Everybody Recommend fgets?
Well, let's take a look at doing the same thing with fscanf and I'll let you be the judge. To begin with, fscanf does not read or include the trailing '\n' with the "%s" format specifier (by default) or when using the character class "%[^\n]" (because it was specifically excluded). So you do not have the ability to read a (1) line with characters and (2) line without characters using the same format string. You either read characters and fscanf succeeds, or you don't and you experience a matching failure.
So as alluded to in the comments, you have to pre-check if the next character in the input buffer is a '\n' character using fgetc (or getc) and then put it back in the input buffer with ungetc if it isn't.
Further adding to your fscanf task, you must independently validate each check, put back, and read every step along the way. This results in quite a number of checks to handle all cases and provide all checks necessary to avoid undefined behavior.
As part of those checks you will need to limit the number of characters you read to one less-than the number of characters in the buffer while capturing the next character to determine if the line was too long to fit. Additional checks are required to handle (without failure) a file with a non-POSIX line end on the final line -- something handled without issue by fgets.
Below is a similar implementation to the fgets code above. Go through and understand why each step it necessary and what each validation prevents against. You may be able to rearrange slightly, but it has been whittled down to close to the bare minimum. After going though it, it should become clear why fgets is the preferred method for handling checks for empty lines (as well as for line oriented input, generally)
#include <stdio.h>
#define BUFSZ 4096
int main (int argc, char **argv) {
int c = 0, r = 0;
size_t n = 1;
char buf[BUFSZ] = "", nl = 0;
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;
}
for (;;) { /* loop until EOF */
if ((c = fgetc (fp)) == '\n') /* check next char is '\n' */
*buf = 0; /* make buf empty-string */
else {
if (c == EOF) /* check if EOF */
break;
if (ungetc (c, fp) == EOF) { /* ungetc/validate */
fprintf (stderr, "error: ungetc failed.\n");
break;
}
/* read line into buf and '\n' into nl, handle failure */
if ((r = fscanf (fp, "%4095[^\n]%c", buf, &nl)) != 2) {
if (r == EOF) { /* EOF (input failure) */
break;
} /* check next char, if not EOF, non-POSIX eol */
else if ((c = fgetc (fp)) != EOF) {
if (ungetc (c, fp) == EOF) { /* unget it */
fprintf (stderr, "error: ungetc failed.\n");
break;
} /* read line again handling non-POSIX eol */
if (fscanf (fp, "%4095[^\n]", buf) != 1) {
fprintf (stderr, "error: fscanf failed.\n");
break;
}
}
} /* good fscanf, validate nl = '\n' or line to long */
else if (nl != '\n') {
fprintf (stderr, "error: line %zu too long.\n", n);
break;
}
} /* output line (or "empty" for empty line) */
printf ("line[%2zu] : %s\n", n++, *buf ? buf : "empty");
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
The Use/Output is identical to above. Look things over and let me know if you have any further questions.

Check if all char values are present in string

I'm currently working on this assignment and I'm stuck. The objective is to read a file and find if these char values exist in the String from the file. I have to compare a String from a file to another String I put in as an argument. However, just as long as each char value is in the String from the file then it "matches".
Example (input and output):
./a.out file1 done
done is in bonehead
done is not in doggie
Example (file1):
bonehead
doggie
As you can see the order in which is compares Strings does not matter and the file also follows one word per line. I've put together a program that finds if the char value is present in the other String but that is only part of the problem. Any idea how to go about this?
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
FILE *f = fopen(argv[1], "r");
char *line = NULL;
size_t len = 0;
ssize_t read;
char *word = argv[2];
if(argc != 3){
printf("./a.out <file> <word>\n");
exit(EXIT_SUCCESS);
}
if(f == NULL){
printf("file empty\n");
exit(EXIT_SUCCESS);
}
// confused what this loop does too
while((read = getline(&line, &len, f)) != -1){
char *c = line;
while(*c){
if(strchr(word, *c))
printf("can't spell \"%s\" without \"%s\"!\n", line, word);
else
printf("no \"%s\" in \"%s\".\n", word, line);
c++;
}
}
fclose(f);
exit(EXIT_SUCCESS);
}
Another approach would simply keep a sum of each character matched in the line read from the file, adding one for each unique character in the word supplied to test, and if the sum is equal to the length of the string made up by the unique characters is the search term, then each of the unique characters in the search term are included in the line read from the file.
#include <stdio.h>
#include <string.h>
#define MAXC 256
int main (int argc, char **argv) {
if (argc < 3 ) { /* validate required arguments */
fprintf (stderr, "error: insufficient input, usage: %s file string\n",
argv[0]);
return 1;
}
FILE *fp = fopen (argv[1], "r");
char line[MAXC] = "";
char *s = argv[2]; /* string holding search string */
size_t slen = strlen(s), sum = 0, ulen;
char uniq[slen+1]; /* unique characters in s */
if (!fp) { /* validate file open */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
memset (uniq, 0, slen+1); /* zero the VLA */
/* fill uniq with unique characters from s */
for (; *s; s++) if (!strchr (uniq, *s)) uniq[sum++] = *s;
ulen = strlen (uniq);
s = argv[2]; /* reset s */
while (fgets (line, MAXC, fp)) { /* for each line in file */
if (strlen (line) - 1 < ulen) { /* short line, continue */
printf ("%s is not in %s", s, line);
continue;
}
char *up = uniq; /* ptr to uniq */
sum = 0; /* reset sum */
while (*up) if (strchr (line, *up++)) sum++; /* count chars */
if (sum < ulen) /* validate sum */
printf ("%s is not in %s", s, line);
else
printf ("%s is in %s", s, line);
}
fclose (fp); /* close file */
return 0;
}
Example Use/Output
$ ./bin/strallcinc dat/words.txt done
done is in bonehead
done is not in doggie
which would work equally well for duplicate characters in the search string. e.g.
$ ./bin/strallcinc dat/words.txt doneddd
doneddd is in bonehead
doneddd is not in doggie
You can decide if you would handle duplicate characters differently, but you should make some determination on how that contingency will be addressed.
Let me know if you have any questions.
confused what this loop does
The while (read ... line obviously reads in lines from your file, placing them in the line variable
*c is a pointer to the start of the variable line and this pointer is incremented by c++, so that each letter in the word from the file is accessed. The while loop will be terminated when *c points to the null terminator (0).
The if (strchr(word ... line is testing if the test word contains one of the letters from the word in the file.
This seems to be the reverse of what you are trying to do - finding if all the letters in the test word can be found in the word from the file.
The printf lines are not sensible because there is no either/or - you need one line to print 'yes' our letters are present and one line to print 'no' at least one letter is not present.
The printf statements should be outside the comparison loop, so that you don't get multiple lines of output for each word. Add a flag to show if any letter does not exist in the word. Set flag to 1 at start, and only change it to 0 when a letter is not present, then use the flag to print one of the two outcome statements.
This code snippet may help
/* set flag to 'letters all present' */
int flag = 1;
/* set pointer c to start of input line */
c = word;
/* test word from file for each letter in test word */
while(*c) {
if(strchr(line, *c) == NULL) {
/* set flag to letter not present */
flag = 0;
break;
}
c++;
}

How to show lines from a text file matching the first five character taking as input?

I am trying to read text file and compare first five character and print the line if the first five characters match. I have text file like this:
03 09 Add this text to file once
03 09 Add this text to file once
12 29 Add this text to file once
So far, I am able to print the content of text file using following code:
while ((c = getc(f)) != EOF) { putchar(c); }
I have input argument as 03 09 as date if the first five characters match then I have to print the whole line.
How to do this?
There are a number of approaches you can take. One of the most straight forward is to read the first line in the file, validate it contains at least 5 character, and save it to a reference buffer to compare each subsequent line against. The strncmp function will allow you to compare the first 'x' number of characters in any two strings. Calling strncmp on the saved buffer and each new line read and comparing the first 5 characters will tell you if the first five chars of each string are the same.
#include <stdio.h>
#include <string.h>
enum { MAXC = 64 }; /* constant for max characters per read */
int main (int argc, char **argv) {
char s1[MAXC] = "";
char buf[MAXC] = "";
size_t idx = 0; /* open given file (default: stdin) */
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, MAXC, fp)) { /* read each line */
if (!idx++) { /* if 1st store s1 */
strcpy (s1, buf);
printf ("%s", s1);
if (strlen (s1) < 5) { /* check 5 chars */
fprintf (stderr, "error: invalid first line.\n");
return 1;
}
else continue; /* read next line */
}
if (strncmp (s1, buf, 5) == 0) /* commpare 1st 5 chars in each line */
printf ("%s", s1);
}
if (fp != stdin) fclose (fp); /* close file */
return 0;
}
Output
$ ./bin/first5 <../dat/first5.txt
03 09 Add this text to file once
03 09 Add this text to file once
note: you can add verifications that each line does not exceed the array size by checking that the strlen of each line is less than your array size (-1 to account for the nul-terminating character) and that last character in each line is a newline. If your line length equals the array size -1 and the last char is not a newline, then additional characters remain in that line that should be read and discarded before attempting to check the next line. That is left to you.
Look over the example and let me know if you have any questions.
Try this:(I am using the fgets() from the previous reply. Do check if that is correct)
while(fgets(line,sizeof(line),file))
{
if((line[0]=='0')&&(line[1]=='3')&&(line[2]==' ')&&(line[3]=='0')&&(line[4]=='9')){
print("%s\n",line);
}
}
This code is pretty basic. It does not care how many chars you have in the line but just checks the first 5 to see if they match. If there are any formatting issues then it will not detect it.
What about this? It takes text from file named input.txt and pattern from standard input. I assumed the maximum length of a line in the file would be 99. You can change it, if you need.
#include <stdio.h>
#include <string.h>
int main()
{
char inp[100],line[100], temp[6];
FILE *file = fopen("input.txt", "r");
fgets(inp,sizeof(inp),stdin);
inp[5]=0;
while(fgets(line,sizeof(line),file))
{
if(strlen(line)<5) continue;
strncpy(temp, line, 5);
temp[5] = 0;
if(!strcmp(inp,temp)) printf("%s", line);
}
fclose(file);
return 0;
}

Wrap long lines from a file for output in console window in c program

Here is my code but it is not making any output to console window. I need to print from output file and also wrap the lines for a particular number of characters, say, 20 character per line:
#include <stdio.h>
#define SIZE 100
int
main (int argc, char *argv[])
{
FILE *fp = NULL;
char line[SIZE] = { 0 };
int i = 0;
for (i = 0; i < argc; i++)
{
printf ("The argc %d is %s\n", i, argv[i]);
}
fp = fopen (argv[1], "r");
if (fp == NULL)
{
printf ("Can't open input file\n");
}
else
{
while (!feof (fp))
{
if (fgets (line, sizeof line, fp))
{
printf ("%s", line);
}
}
}
if (fclose (fp) != 0)
{
printf ("Error closing file\n");
}
return 0;
}
Your code makes no attempt to do line wrapping.
What you need to do (broadly speaking) is as follows:
Maintain a temporary buffer
Read each line of the file
If the line read is \n (only), output the buffer followed by \n, and go to 2.
If the buffer is empty, THEN replace the buffer by the line that is read ELSE append a space to the buffer and append the line read to the buffer.
If the buffer has more than 20 characters (if you are line wrapping on twenty characters), find the 20th character, and count backwards until you find a white space. Output the buffer up to that character with a \n, and replace the buffer by the data in the buffer after the whitespace.
At the end, output any remaining characters in the buffer followed by \n.
Implementing this is left as an exercise for the reader (we can't do all your homework for you).
Please also see #JoachimPileborg's wise advice re reading 'Why is while ( !feof (file) ) always wrong?'

Resources