Why "scanf" works but fgets doesn't works in C? - c

Look here, those two programms should be equivalent in my opinion. But obviously they aren't, as the first programm works and the second doesn't. Can someone explain to me, why fgets() doesn't do the job?
// FIRST PROGRAM : WORKS FINE
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *stream;
char fileName[67];
scanf("%s", fileName);
printf("%s", fileName);
stream = fopen(fileName, "r");
char ch;
if(stream){
ch = fgetc(stream);
while(!feof(stream)){
putchar(ch);
ch = fgetc(stream);
}
fclose(stream);
}
}
// SECOND PROGRAM: DOES NOT WORK
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *stream;
char fileName[67];
fgets(fileName, 67, stdin);
printf("%s", fileName);
stream = fopen(fileName, "r");
char ch;
if(stream){
ch = fgetc(stream);
while(!feof(stream)){
putchar(ch);
ch = fgetc(stream);
}
fclose(stream);
}
}
I enter "test.txt" into the console both times and press enter then. Of course test.txt exists in the right directory

The reason is that fgets() retains the newline entered. You can verify it is there by altering your print statement to
printf("[%s]", filename);
when the ] will appear on the next line. You can remove the trailing newline like this
#include <string.h>
...
filename [ strcspn(filename, "\r\n") ] = 0;

The main problem you experienced is correctly solved by Weather Vane, but I want to point another problem with your code: the loops for reading and writing the contents of the file are incorrect. Testing the end of file with feof(stream) leads to incorrect behaviour most of the time. In your case, a read error from stream will cause your program to loop endlessly, writing 0xFF bytes to stdout.
There is a much simpler and idiomatic way to implement this loop:
if (stream) {
int ch;
while ((ch = fgetc(stream)) != EOF) {
putchar(ch);
}
fclose(stream);
}
As you can see, it is simpler and correctly tests for EOF at the right time, when input fails. It stores the return value of fgetc() into an int variable capable of holding all possible return values from fgetc(). Using an int is necessary because comparing a char value to EOF either always fails if the char type is unsigned or potentially gives false positives if char is signed and has value '\377'.

Related

It takes very long time to read '.txt file' how can i solve this problem? ( C )

there is very long "dict.txt" file.
the size of this file is about 2400273(calculated by fseek, SEEK_END)
this file has lots of char like this 'apple = 사과'(simillar to dictionary)
Main problem is that reading file takes very long time
I couldn't find any solution to solve this problem in GOOGLE
The reason i guessed is associated with using fgets() but i don't know exactly.
please help me
here is my code written by C
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int line = 0;
char txt_str[50];
FILE* pFile;
pFile = fopen("dict_test.txt", "r");
if (pFile == NULL) {
printf("file doesn't exist or there is problem to open your file\n");
}
else {
do{
fgets(txt_str, 50, pFile);;
line++;
} while (txt_str != EOF);
}
printf("%d", line);
}
Output
couldn't see result because program was continuosly running
Expected
the number of lines of this txt file
Major
OP's code fail to test the return value of fgets(). Code needs to check the return value of fgets() to know when to stop. #A4L
do{
fgets(txt_str, 50, pFile);; // fgets() return value not used.
Other
Line count should not get incremented when fgets() returns NULL.
Line count should not get incremented when fgets() read a partial line. (I. e.) the line was 50 or longer. Reasonable to use a wider than 50 buffer.
Line count may exceed INT_MAX. There is always some upper bound, yet trivial to use a wider type.
Good practice to close the stream.
Another approach to count lines would use fread() to read chunks of memory and then look for start of lines. (Not shown)
Recommend to print a '\n' after the line count.
int main(void) {
FILE* pFile = fopen("dict_test.txt", "r");
if (pFile == NULL) {
printf("File doesn't exist or there is problem to open your file.\n");
return EXIT_FAILURE;
}
unsigned long long line = 0;
char txt_str[4096];
while (fgets(txt_str, sizeof txt_str, pFile)) {
if (strlen(txt_str) == sizeof txt_str - 1) { // Buffer full?
if (txt_str[sizeof txt_str - 1] != '\n') { // Last not \n?
continue;
}
}
line++;
}
fclose(pFile);
printf("%llu\n", line);
}
fgets returns NULL on EOF.
You are never assigning the result of
fgets(txt_str, 50, pFile);
to txt_str, your program never sees the end of the file and thus enters an endless loop.
try something like this:
char* p_str;
do{
p_str = fgets(txt_str, 50, pFile);
} while (p_str != NULL);

C Using char* in fscanf causing error Segmentation fault: 11

I am new to C and I came across an issue when using fscanf to read all strings from a .txt file.
The code is as follow:
#include <stdlib.h>
#include <stdio.h>
int main() {
FILE *spIn;
char *numIn;
spIn = fopen("data.txt", "r");
if (spIn == NULL) {
printf("Can't Open This File \n");
}
while ((fscanf(spIn, "%s", numIn)) == 1) {
printf("%s\n", numIn);
};
fclose(spIn);
return 1;
}
This throws an error: Segmentation fault: 11.
The original data on txt file is:
1 2 345 rrtts46
dfddcd gh 21
789 kl
a mix of ints, strings, white space and newline characters.
At least 4 candidate undefined behaviors (UB) that could lead to a fault of some kind.
Code fails to pass to fscanf(spIn,"%s",numIn) an initialized pointer.
Code calls fscanf() even if fopen() fails.
Code calls fclose() even if fopen() fails.
No width limit in fscanf(spIn,"%s",numIn)), worse than gets().
Text files really do not have strings ('\0' terminated data) nor int, they have lines (various characters with a '\n' termination).
To read a line in and save as a string, use fgets(). Do not use fscanf() to read lines of data.
#include <stdlib.h>
#include <stdio.h>
int main() {
FILE *spIn = fopen("data.txt", "r");
if (spIn == NULL) {
printf("Can't Open This File \n");
} else {
char buf[100];
while (fgets(buf, sizeof buf, spIn)) {
printf("%s", buf);
}
fclose(spIn);
}
}
char* numIn is a pointer, and it is uninitalized, you can't really store anything in it, you need to either allocate memory for it or make it point to some valid memory location:
#include<stdlib.h> // for malloc
char* numIn = malloc(100); // space for 99 char + null terminator byte
//...
while ((fscanf(spIn, "%99s", numIn)) == 1)
{
printf("%s\n",numIn);
};
Or:
char str[100];
char *numIn = str;
Which in this small code makes little sense, you should probably make numIn a fixed size array to begin with:
char numIn[100];
Note that that you should use a width specifier in *scanf to avoid buffer overflow. This still has a problem though, it will read word by word, instead of line by line.
Looking at your input file, using fgets seems like a better option, it can read complete lines, including spaces:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *spIn;
char numIn[100];
spIn = fopen("data.txt", "r");
if (spIn != NULL)
{
while ((fgets(numIn, sizeof numIn, spIn)))
{
numIn[strcspn(numIn, "\n")] = '\0'; // removing \n
printf("%s\n", numIn);
}
fclose(spIn);
}
else
{
perror("Can't Open This File");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Since fgets also parses the \n character, I'm removing it with strcspn.
Though you do verify the return value of fopen the execution continues even if it fails to open, I also addressed that issue.

Reading files with the same extension in a directory and count their lines

I am having this problem with my code. I've been trying to open files that have the same extension and read the number of lines in the file that is in the directory.
So, here is what I've done:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
int countLines(char name[]);
int main()
{
struct dirent *de;
DIR *dr=opendir(".");
char check[16]=".nkt";
int i;
char name[64];
int count=0;
if(dr==NULL)
{
printf("Didn't open!");
return 0;
}
while((de=readdir(dr))!=NULL)
{
if((strstr(de->d_name, check))!=NULL)
{
strcpy(name, de->d_name);
countLines(name);
}
}
closedir(dr);
return 0;
}
int countLines(char name[])
{
FILE *fp;
fp=fopen(name,"r");
char ch;
int lines=0;
while(!feof(fp))
{
ch=fgetc(fp);
if(ch=='\n')
{
lines++;
}
}
fclose(fp);
printf("%d\n", lines);
}
and the result that I am getting is always like :
2
2
2
Even though every file has 54 lines.
Would gladly appreciate some help.
PS. The extension is .nkt
The countLines() function you show is stepping into several traps.
fgetc() returns int not char by intention. It does this to be able to return the End-of-File state, aside all other possible character values. A simple char cannot do this.
The use of feof() to identify the End-of-File fails as the EOF indicator is set only after the last read hitting the end of the file has been completed. So a loop steered using feof() typically iterated one time to often.
A detailed discussion on this is here.
A text file's last line not necessarily carries an End-of-File indicator, but you mostly likely still want count that line. Special logic needs to be applied to cover this case.
A possible implementation of a function taking care off all those issue mentioned above might look like this:
#include <stdio.h>
/* Returns the number of lines inside the file named file_name
or -1 on error. */
long count_lines(const char * file_name)
{
long lines = 0;
FILE * fp = fopen(file_name, "r"); /* Open file to read in text mode. */
if (NULL == fp)
{
lines = -1;
}
else
{
int previous = EOF;
for (int current; (EOF != (current = fgetc(fp)));)
{
if ('\n' == current)
{
++lines;
}
previous = current;
}
if (ferror(fp)) /* fgetc() returns EOF as well if an error occurred.
This call identifies that case. */
{
lines = -1;
}
else if (EOF != previous && '\n' != previous)
{
++lines; /* Last line missed trailing new-line! */
}
fclose(fp);
}
return lines;
}
Regarding the discussion about different End-of-Line indicators inside the question's comment section:
The End-of-Line indicator for text files is implemented differently on different platforms (UNIX: '\n' vs. Windows: \r\n vs. ... (https://en.wikipedia.org/wiki/Newline)).
To manoeuvre around this the C library function fopen() by default opens a file in so called "text-mode". If opened this way the C implementation takes care that each line's end is returned as a single '\n' character, the so called "new-line" character. Please note (as mentioned above under 3.) that for the last line there might be no End-of-Line indicator at all.

Reading information from a file in C language

So I have the txt file from which I need to read the number of students written in that file, and because every student is in separate line, it means that I need to read the number of lines in that document. So I need to:
Print all lines from that document
Write the number of lines from that document.
So, I write this:
#include "stdafx.h"
#include <stdio.h>
int _tmain(int argc, _TCHAR* Argo[]){
FILE *student;
char brst[255];
student = fopen("student.txt", "r");
while(what kind of condition to put here?)
{
fgetc(brst, 255, (FILE*)student);
printf("%s\n", brst);
}
return 0;
}
Ok, I understand that I can use the same loop for printing and calculating the number of lines, but I can't find any working rule to end the loop. Every rule I tried caused an endless loop. I tried brst != EOF, brst != \0. So, it works fine and print all elements of the document fine, and then it start printing the last line of document without end. So any suggestions? I need to do this homework in C language, and I am using VS 2012 C++ compiler.
OP's code is close but needs to use fgets() rather than fgetc() and use the return value of fgets() to detect when to quit, it will be NULL #Weather Vane. Also add a line counter.
#include <stdio.h>
int main(void) {
FILE *student = fopen("student.txt", "r");
unsigned line_count = 0;
if (student) {
char brst[255];
// fgetc(brst, 255, (FILE*)student);
while (fgets(brst, sizeof brst, student)) {
line_count++;
printf("%u %s", line_count, brst);
}
fclose(student);
}
printf("Line Count %u\n", line_count);
return 0;
}
Try this:
#include "stdafx.h"
#include <stdio.h>
int _tmain(int argc, _TCHAR* Argo[]){
FILE *student;
char brst[255];
char* result = NULL;
//Ensure file open works, if it doesn't quit
if ((student = fopen("student.txt", "r")) == NULL)
{
printf("Failed to load file\n");
return 1;
}
//Read in the file
for ( (result = fgets( brst, sizeof(brst), student));
!feof(student);
(result = fgets( brst, sizeof(brst), student)) )
{
if ( result == NULL ) break; //I've worked on embedded systems where this actually ment waiting on data, not EOF, so a 'continue' would go here instead of break in that case
printf("%s\n", brst);
}
fclose( student );
return 0;
}
feof() is only true after you've read past the end of the file. Using a for with two identical reads, and feof() on the conditional is a simple way to ensure you read the file as expected.
Use feof() to check for an eof condition.
You are correctly reading the file line-by-line, but use fgets(), not fgetc() - and the cast is not needed.
Then use sscanf() to assign the line data to variables (or some "safe" form of it).

Reading a file from stdin

It's been years since I programmed in C, and so I've been struggling a lot just to do a simply "get filename & path from stdin, read file, print file to stdout" task, which I know shouldn't be that hard but ya. Here is my code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void) {
int c;
FILE *file;
//scanf("%s", filename);
char *filename;
filename = (char *)malloc(200 * sizeof(char));
read(STDIN_FILENO, filename, 200);
printf("%s", filename);
file = fopen(filename, "r");
if (file) {
while ((c = getc(file)) != EOF)
putchar(c);
fclose(file);
} else {
printf("File not found.");
}
printf("\n");
return(0);
}
I my code continues to simply print out File not found., when I know for a fact my file path and everything is correct (not only because I literally drop it and past it into terminal from my folder with Mac OSX El Capitan - what a lovely feature, but also) because I had a different version of this program using scanf which found the file and read it perfectly fine, (as you can see I have it commented out on my code).
There is another program I'm writing that just uses this one, and I got rid of the scanf because I think it was negatively affecting other things in that program, so I want to be able to use read()
If anyone has any advice on how I can fix this or why this isn't working, that would be greatly appreciated as I've been at this for hours already and would very much like to move on to my actual program that I need to code!
THANKS A BUNCH
You must remove the '\n' new line character that is being read and stored into the filename buffer.
One of the many was to do it is include string.h and after reading the filename
char *newline = strchr(filename, '\n');
if (newline != NULL)
*newline = '\0';
Also, use fgets() instead of read() because that way the program is more portable. And more importantly, read() will not add the null terminator which is very important in order to use the buffer as a string — to pass it to fopen() for example — correctly. If you want to use read try something like this
ssize_t length;
char filename[200];
length = read(STDIN_FILENO, filename, sizeof(filename) - 1);
if (length <= 0)
return -1; // No input or input error
if (filename[length] == '\n')
filename[--length] = '\0';
else
filename[length] = '\0';
But otherwise, try this which is simpler
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
FILE *file;
char filename[200];
char *newline;
if (fgets(filename, sizeof(filename), stdin) == NULL)
return -1; // Input error / EOF
newline = strchr(filename, '\n');
if (newline) // ? is a newline present?
*newline = '\0';
printf("**%s**\n", filename); // ** will help checking for
// the presence of white spaces.
file = fopen(filename, "r");
if (file) {
int chr;
while ((chr = fgetc(file)) != EOF)
fputc(chr, stdout);
fclose(file);
} else {
printf("File not found.");
}
printf("\n");
return 0;
}

Resources