I am trying to write a simple C program that loads a text-file, prints the first line to screen, waits for the user to press enter and then prints the next line, and so on.
As only argument it accepts a text-file that is loaded as a stream "database". I use the getline()-function for this, according to this example. It compiles fine, successfully loads the text-file, but the program never enters the while-loop and then exits.
#include <stdio.h>
#include <stdlib.h>
FILE *database = NULL; // input file
int main(int argc, char *argv[])
{
/* assuming the user obeyed syntax and gave input-file as first argument*/
char *input = argv[1];
/* Initializing input/database file */
database = fopen(input, "r");
if(database == NULL)
{
fprintf(stderr, "Something went wrong with reading the database/input file. Does it exist?\n");
exit(EXIT_FAILURE);
}
printf("INFO: database file %s loaded.\n", input);
/* Crucial part printing line after line */
char *line = NULL;
size_t len = 0;
ssize_t read;
while((read = getline(&line, &len, database)) != -1)
{
printf("INFO: Retrieved line of length %zu :\n", read);
printf("%s \n", line);
char confirm; // wait for user keystroke to proceed
scanf("%c", &confirm);
// no need to do anything with "confirm"
}
/* tidy up */
free(line);
fclose(database);
exit(EXIT_SUCCESS);
}
I tried it with fgets() -- I can also post that code --, but same thing there: it never enters the while-loop.
It might be something very obvious; I am new to programming.
I use the gcc-compiler on Kali Linux.
Change your scanf with fgetline using stdin as your file parameter.
You should step through this in a debugger, to make sure your claim that it never enters the while loop is correct.
If it truly never enters the while loop, it is necessarily because getline() has returned -1. Either the file is truly empty, or you have an error reading the file.
man getline says:
On success, getline() and getdelim() return the number of
characters
read, including the delimiter character, but not including the termi‐
nating null byte ('\0'). This value can be used to handle embedded
null bytes in the line read.
Both functions return -1 on failure to read a line (including end-of-
file condition). In the event of an error, errno is set to indicate
the cause.
Therefore, you should enhance your code to check for stream errors and deal with errno -- you should do this even when your code works, because EOF is not the only reason for the function
to return -1.
int len = getline(&line, &len, database);
if(len == -1 && ferror(database)) {
perror("Error reading database");
}
You can write more detailed code to deal with errno in more explicit ways.
Unfortunately handling this thoroughly can make your code a bit more verbose -- welcome to C!
Related
Today I decided to learn to code for the first time in my life. I decided to learn C. I have created a small program that checks a txt file for a specific value. If it finds that value then it will tell you that that specific value has been found.
What I would like to do is that I can put multiple files go through this program. I want this program to be able to scan all files in a folder for a specific string and display what files contain that string (basically a file index)
I just started today and I'm 15 years old so I don't know if my assumptions are correct on how this can be done and I'm sorry if it may sound stupid but I have been thinking of maybe creating a thread for every directory I put into this program and each thread individually runs that code on the single file and then it displays all the directories in which the string can be found.
I have been looking into threading but I don't quite understand it. Here's the working code for one file at a time. Does anyone know how to make this work as I want it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//searches for this string in a txt file
char searchforthis[200];
//file name to display at output
char ch, file_name[200];
FILE *fp;
//Asks for full directory of txt file (example: C:\users\...) and reads that file.
//fp is content of file
printf("Enter name of a file you wish to check:\n");
gets(file_name);
fp = fopen(file_name, "r"); // read mode
//If there's no data inside the file it displays following error message
if (fp == NULL)
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
//asks for string (what has to be searched)
printf("Enter what you want to search: \n");
scanf("%s", searchforthis);
char* p;
// Find first occurrence of searchforthis in fp
p = strstr(searchforthis, fp);
// Prints the result
if (p) {
printf("This Value was found in following file:\n%s", file_name);
} else
printf("This Value has not been found.\n");
fclose(fp);
return 0;
}
This line,
p = strstr(searchforthis, fp);
is wrong. strstr() is defined as, char *strstr(const char *haystack, const char *needle), no file pointers in it.
Forget about gets(), its prone to overflow, reference, Why is the gets function so dangerous that it should not be used?.
Your scanf("%s",...) is equally dangerous to using gets() as you don't limit the character to be read. Instead, you could re-format it as,
scanf("%199s", searchforthis); /* 199 characters + \0 to mark the end of the string */
Also check the return value of scanf() , in case an input error occurs, final code should look like this,
if (scanf("%199s", searchforthis) != 1)
{
exit(EXIT_FAILURE);
}
It is even better, if you use fgets() for this, though keep in mind that fgets() will also save the newline character in the buffer, you are going to have to strip it manually.
To actually perform checks on the file, you have to read the file line by line, by using a function like, fgets() or fscanf(), or POSIX getline() and then use strstr() on each line to determine if you have a match or not, something like this should work,
char *p;
char buff[500];
int flag = 0, lines = 1;
while (fgets(buff, sizeof(buff), fp) != NULL)
{
size_t len = strlen(buff); /* get the length of the string */
if (len > 0 && buff[len - 1] == '\n') /* check if the last character is the newline character */
{
buff[len - 1] = '\0'; /* place \0 in the place of \n */
}
p = strstr(buff, searchforthis);
if (p != NULL)
{
/* match - set flag to 1 */
flag = 1;
break;
}
}
if (flag == 0)
{
printf("This Value has not been found.\n");
}
else
{
printf("This Value was found in following file:\n%s", file_name);
}
flag is used to determine whether or not searchforthis exists in the file.
Side note, if the line contains more than 499 characters, you will need a larger buffer, or a different function, consider getline() for that case, or even a custom one reading character by character.
If you want to do this for multiple files, you have to place the whole process in a loop. For example,
for (int i = 0; i < 5; i++) /* this will execute 5 times */
{
printf("Enter name of a file you wish to check:\n");
...
}
I have the following program which prints text from a file:
#include<stdio.h>
int main(void) {
int ch;
while ((ch=getchar()) != EOF) {
putchar(ch);
}
printf("Enter a character now...\n");
// possible to prompt input now from a user?
ch = getchar();
printf("The character you entered was: %c\n", (char) ch);
}
And running it:
$ ./io2 < file.txt
This is a text file
Do you like it?
Enter a character now...
The character you entered was: �
After this, how would I get a user to enter in a character. For example, if I were to do getchar() (when not redirecting the file to stdin)?
Now it seems it just will keep printing the EOF character if I keep doing getchar() at the end.
My previous suggestion won't work because the cat command will just join both the file and stdin as one and supply that to your program and you will eventually reach the same conclusion.
If your program needs the file, it should just read from it directly, then get the rest of its input from standard input...
#include<stdio.h>
#include<stdlib.h>
int main(void) {
int ch;
FILE* file = fopen("file.txt", "r");
if (file == NULL) {
perror("fopen");
return EXIT_FAILURE;
}
while ((ch=fgetc(file)) != EOF) {
putchar(ch);
}
fclose(file);
... // now just read from stdin
return EXIT_SUCCESS;
}
You can call clearerr(stdin) to clear the end-of-file (and error) conditions:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int c;
/* Consume standard input */
while ((c = getchar()) != EOF) {
putchar(c);
}
/* Clear the error condition */
clearerr(stdin);
printf("Please provide more input.\n");
fflush(stdout);
/* Consume more standard input */
while ((c = getchar()) != EOF) {
putchar(c);
}
return EXIT_SUCCESS;
}
However, this is the wrong approach. If you run echo Hello | ./io2, the program will not wait for additional input, because standard input is provided by echo, and is no longer connected to the terminal.
The proper approach is to use command-line parameters to specify the file name, and read it using a separate FILE handle:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
FILE *in;
int c;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
fprintf(stderr, " %s FILENAME\n", arg0);
fprintf(stderr, "\n");
fprintf(stderr, "This program reads and outputs FILENAME, then\n");
fprintf(stderr, "prompts and reads a line from standard input.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
/* Open specified file. */
in = fopen(argv[1], "r");
if (!in) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
/* Read and output file, character by character ("slow") */
while ((c = getc(in)) != EOF) {
putchar(c);
}
/* Check if the EOF indicated an error. */
if (ferror(in)) {
fprintf(stderr, "%s: Read error.\n", argv[1]);
return EXIT_FAILURE;
}
/* Close the input file. Be nice, and check for errors. */
if (fclose(in)) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
/* Prompt for new input. */
printf("Please input something.\n");
fflush(stdout);
while (1) {
c = getchar();
if (c == EOF || c == '\n' || c == '\r')
break;
putchar(c);
}
printf("All done.\n");
return EXIT_SUCCESS;
}
There are several things worthy of note here:
argv[0] is the command itself (./io2 in your example). The first command-line argument is argv[1]. Because argc is the number of entries in argv, argc == 1 means there are no arguments; argc == 2 means there is one argument, and so on.
If argc == 2, then argv[0] and argv[1] are valid. In POSIXy systems like Linux and BSDs and Mac, and standard C libraries conforming to C11, argv[argc] == NULL, and is safe to access.
The if line relies on C logic rules. In particular, if you have expr1 || expr2, and expr1 is true, expr2 is never evaluated.
This means that if argc != 2, the strcmp() checks are not evaluated at all.
The !strcmp(argv[1], "-h") is true if and only if argv[1] matches -h.
Thus, the if line reads, "if argc says we don't have exactly two elements in argv array, or we have and argv[1] matches -h, or we have and argv[2] matches --help, then".
It is often possible to execute a program without any arguments in POSIXy systems, for example via execl("./io2", NULL, NULL) or by some other nonstandard trick. This means that it is technically possible for argc to be zero. In that case, we don't know how this program was executed.
The value of arg0 is a ternary expression which essentially reads, "if argc says we we should have at least one element in argv array, and argv array exists, and the first element in argv array exists, and the first character in that first element is not end of string mark, then arg0 is the first element in argv array; otherwise, arg0 is (this)."
This is only needed because I like printing the usage when run with -h or --help as the first parameter. (Almost all command-line programs in POSIXy systems do this.) The usage shows how to run this program, and for this, I want to use the same command that was used to run this program; hence arg0.
When getc()/getchar()/fgetc() returns EOF, it means that there is no more input in the stream. This can happen because of end of stream, or because a read error occurred.
I like to carefully check for errors. Some consider it "useless", but to me, error checking is important. As an user, I want to know – no, I need to know if my storage media is producing errors. Therefore, the ferror(in) check is important to me. It is true (nonzero) only if there was a read/write error (I/O error) when accessing the stream in.
Similarly, it is possible for fclose(in) to report a delayed error. I do not believe it is possible to happen for a read-only stream, but it definitely is possible for streams we write to, because the C standard library buffers stream data, and the final underlying write operation can occur when we are closing the stream handle. Even the man 3 fclose man page explicitly says this is possible.
Some programmers say that it is not useful to check for fclose() errors, because they are so rare. To me, as an user, it is. I want the programs I use to report the errors they detect, instead of assuming "eh, it's so rare I won't bother checking or reporting those"*.
By default, standard output (stdout) is line-buffered, so technically the fflush(stdout) is not needed. (The fact that the previous printf() ends with a newline, \n, means that that printf() should cause the standard output to be flushed.)
Flushing a stream means ensuring the C library actually writes its internal buffer to the output file or device. Here, we definitely want to be sure the user sees the prompt, before we start waiting for input. Thus, while the fflush(stdout) is technically not needed, here it also provides us human programmers a reminder that at this point, we do need the stdout stream to be flushed to the actual output device (terminal).
It is often useful to redirect program output to a file, or via a pipe as input to another program. Because of this, I like to use the standard error (stderr) for error messages and the usage information.
If the user runs the program incorrectly, or an error occurs, with the output redirected to a file or piped to another program, they will typically still see the standard error output. (It is possible to redirect standard error too, though.)
I am trying to write user input to a txt file using a while loop.
But for some reason that I don't know, the while loop doesn't stop by clicking 'enter'.
when I click 'enter', I see "while loop executed" so I know the while loop iterated again even though I wanted to stop the while loop.
How to do it in a right way?
P.S: I use Microsoft Visual Studio recommended by my professor.
#include <stdio.h>
#include <string.h>
void main()
{
//writing to file
char write[100];
char fileName[100];
printf("give a name for file: ");
gets_s(fileName,99);
strcat(fileName,".txt");
FILE* pF = fopen(fileName, "w");
printf("what needs to be written?\n: ");
while(gets_s(write,99) != NULL)
{
printf("whileloop executed\n");
fprintf(pF,"%s\n", write);
}
fclose(pF);
}
Per Microsoft's gets_s() documentation, gets_s() returns NULL on end-of-file and error conditions:
Return Value
Returns buffer if successful. A NULL pointer indicates an error or
end-of-file condition. Use ferror or feof to determine which one
has occurred.
Hitting the enter key does not close the input stream and cause an end-of-file condition. It just places a zero-length line terminated with a newline in the input stream.
To do that on Windows from a terminal, one usually has to enter the CTRL-Z key combination.
You need to do some handling there as gets_s() will return and empty string when enter is hit. You can write an extra function to handle that:
size_t my_gets(char *buf,size_t len)
{
size_t ret=0;
if(gets_s(buf,len))
ret = strlen(buf);
return ret;
}
Then change your loop to:
while(my_gets(write,99))
{
...
P.S.- You should avoid calling variables with the same name of system functions (i.e. write).
It`s a file copying program.
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
int main()
{
int fd1,fd2, ndata;
char data[128];
char *openname[1], *creatname[1];
write(1, "Write open file name\n", 24); //user input
read(0, openname, 30 );
write(1, "Write creat file name\n", 25); //user input
read(0, creatname,30);
if((fd1 = open(openname, 0666))<0)
{
perror("cannot open the file");
exit(1);
}
if((fd2 = creat(creatname,0666))<0)
{
perror("cannot create the file");
exit(1);
}
while((ndata = read(fd1, data, 128))>0)
{
if((write(fd2, data, ndata))!=ndata)
{
perror("cannot write the file");
exit(1);
}
}
close(fd1);
close(fd2);
write(1, "File copy is done.",19);
return 0;
}
This code ain`t work. This code print the error message:
cannot open the file.
but if i change the code to this :
if((fd1 = open("copy.c", 0666))<0)
and this :
if((fd2 = creat("real.c",0666))<0)
worked well.
Why this error happend? Please answer.
Your declarations of openname and creatname are incorrect. They should be:
char openname[31], creatname[31];
read() does not add a null terminator to the input, you need to add it. read() returns the number of bytes read. So it should be:
int nread = read(0, openname, sizeof openname -1);
openname[nread-1] = '\0'; // subtract 1 to overwrite the newline
The type of openname and creatname is wrong, and gcc -Wall -g would have warned you. Declare e.g. char openname[256];
And you should use fgets(openname, sizeof(openname), stdin); to read it.
If you insist on using read, take care of the newline (if any) and add a zero terminating byte.
Learn also to use the gdb debugger.
read is very low level. In this case, it reads 30 bytes, including your enter key and also without a terminating null-byte. So the filename won't be what you think you've entered, it will contain additional garbage (and could even make your program crash due to the missing null-termination). You want to use fgets or readline instead.
In a nutshell, by using read() to input the file names, you are making this unnecessarily hard for yourself: it does not terminate the input with NUL, is not guaranteed to read the number of characters you expect, etc.
My advice would be to stick with scanf() or fgets().
While doing filing im stuck here.The condition of the while loop is not working.The compiler says cannot convert int to FILE*.
while(pFile!=EOF);
Should i typecase the pFile to int?I tried that but it did not worked.Thanks in advance.
The complete code is:
int main()
{
char ch;
char name[20];
FILE *pFile;
int score;
pFile=fopen("database.txt","r");
if(pFile!=NULL)
{
while(pFile!=EOF);
{
fscanf(pFile,"%c",ch);
}
}
else
printf("Cant open the file.......");
fclose(pFile);
return 0;
}
First, you do not want to use while (!feof(pFile)) -- ever! Doing so will almost inevitably lead to an error where the last data you read from the file appears to be read twice. It's possible to make it work correctly, but only by adding another check in the middle of the loop to exit when EOF is reached -- in which case, the loop condition itself will never be used (i.e., the other check is the one that will actually do the job of exiting the loop).
What you normally do want to do is check for EOF as you read the data. Different functions indicate EOF in different ways. fgets signals failure (including EOF) by returning NULL. Most others (getc, fgetc, etc.) do return EOF, so you typically end up with something like this:
int ch; // Note, this should be int, NOT char
while (EOF != (ch=getc(pFile)))
process(ch);
or:
char buffer[MAX_LINE_SIZE];
while (fgets(buffer, sizeof(buffer), pFile))
process(buffer);
With scanf, checking for success is a little more complex -- it returns the number of successful conversions, so you want to make sure that matches what you expected. For example:
while (1 == fscanf(fPfile, "%d", &input_number))
process(input_number);
In this case I've used 1 because I specified 1 conversion in the format string. It's also possible, however, for conversion to fail for reasons other than EOF, so if this failes, you'll frequently want to check feof(pFile). If it returns false, do something like reading the remainder of the line, showing it to the user in a warning message, and then continuing to read the rest of the file.
It depends what pFile and EOF are defined as, but I will asssume that pFile is a *FILE, and EOF is from stdio.h. Then I guess you should do something like:
#include <stdlib.h>
#include <stdio.h>
#define FILENAME "file.txt"
int main(void) {
FILE *pFile;
int ch;
pFile = fopen(FILENAME,"r");
if (pFile) {
while ((ch = getc(pFile)) != EOF) {
printf("Read one character: %c\n", ch);
}
close(pFile);
return EXIT_SUCCESS;
} else {
printf("Unable to open file: '%s'\n", FILENAME);
return EXIT_FAILURE;
}
}
which yields
$ echo "abc" > file.txt
$ /tmp/fileread
Read one character: a
Read one character: b
Read one character: c
Read one character:
# last character being a linefeed
Assuming pFile is your file handle, this doesn't change as you read from the file. EOF is returned by e.g. fgetc(). See e.g. http://www.drpaulcarter.com/cs/common-c-errors.php#4.2 for common ways to solve this.
here is correct way:
c = getc(pFile);
while (c != EOF) {
/* Echo the file to stdout */
putchar(c);
c = getc(pFile);
}
if (feof(pFile))
puts("End of file was reached.");
else if (ferror(pFile))
puts("There was an error reading from the stream.");
else
/*NOTREACHED*/
puts("getc() failed in a non-conforming way.");
fclose(pFile);
pFile is a pointer to a file. EOF is usually defined as -1, a signed integer.
What you should do is fopen, make sure pFile != NULL, then call some function on the file handle until that function returns EOF. A pointer will (or rather, should) never be EOF. But a function acting on that pointer may return EOF.
I'm guessing you want to keep looping while you haven't hit end-of-file. In that case, you are looking for this:
while (!feof(pFile))
{
...
}
That said, this is still not quite correct. feof will only return true once it tries to read beyond the end of the file. This means feof can return false and yet there is no more data to read. You should really try your operation and only check for end of file if it fails:
char buffer[SIZE];
while (fgets(buffer, sizeof(buffer), pFile))
{
...
}
if (!feof(pFile))
{
// fgets failed for some reason *other* then end-of-file
}