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.)
Related
I have a question about I/O in C language, how can I make a difference to know if the lecture of my file has ended or if the data can't be read (or has a problem) as in the both cases, fscanf returns EOF ?
Don´t rely only on the return value of fscanf(), rely beside this one on feof() and ferror() after the call to fscanf():
FILE* file;
if((file == fopen("file.txt","r")) == NULL)
{
fprintf(stderr, "File could not be opened!");
return EXIT_FAILURE;
}
char buf;
/******************************************************************************/
while(fscanf(file,"%c",buf) == 1) { // checks if an error was happen, else
// iterate to catching characters.
/* handling of read character */
}
if(ferror(file)) // checks if an I/O error occurred.
{
// I/O error handling
fprintf(stderr,"Input/Output error at reading file!");
clearerr(file);
// Further actions
}
else if(feof(file)) // checks if the end of the file is reached.
{
// end of file handling
fprintf(stderr,"Reached End of File!");
clearerr(file);
// Further actions
}
/******************************************************************************/
if(fclose(file) != 0)
{
fprintf(stderr, "File could not be closed properly!");
return EXIT_FAILURE;
}
As per fscanf() return value:
ISO/IEC 9899:2017
§ 7.21.6.2 - 16 - The fscanf function returns the value of the macro EOF if an input failure occurs before the first conversion (if any) has completed. Otherwise, the function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure.
EOF is a macro with the value of -1, by itself it's not distinguishable as for the reasons why it occurs.
For this distinction § 7.21.6.2 - 19 recommends the use of feof() for end-of-file and ferror() for I/O error:
EXAMPLE 3 To accept repeatedly from stdin a quantity, a unit of measure, and an item name:
#include<stdio.h>
/*...*/
int count; floatquant;
charunits[21], item[21];
do {
count = fscanf(stdin, "%f%20sof%20s", &quant, units, item);
fscanf(stdin,"%*[^\n]");
} while(!feof(stdin) && !ferror(stdin));
My usual approach when reading formated input, is to check the inputed values. For a sample input of 2 integers you can do something like:
int a, b;
FILE* file;
//open file to read
while(fscanf(file, "%d %d", &a, &b) == 2){ //read each 2 integers in the file, stop when condition fails, i.e. there are nothing else to read or the read input is not an integer
//...handle inputs
}
This kind of read is safe and addresses all failure scenarios since it works for bad input and for "end of file".
I wrote a file parser for a project that parses a file provided on the command line.
However, I would like to allow the user to enter their input via stdin as well, but exclusively through redirection via the command line.
Using a Linux based command prompt, the following commands should yield the same results:
./check infile.txt (Entering filename via command line)
./check < infile.txt
cat infile.txt | ./check
The executable should accept a filename as the first and only command-line argument. If no filename is specified, it should read from standard input.
Edit: I realized how simple it really was, and posted an answer. I will leave this up for anyone else who might need it at some point.
This is dangerously close to "Please write my program for me". Or perhaps it even crossed that line. Still, it's a pretty simple program.
We assume that you have a parser which takes a single FILE* argument and parses that file. (If you wrote a parsing function which takes a const char* filename, then this is by way of explaining why that's a bad idea. Functions should only do one thing, and "open a file and then parse it" is two things. As soon as you write a function which does two unrelated things, you will immediately hit a situation where you really only wanted to do one of them (like just parse a stream without opening the file.)
So that leaves us with:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "myparser.h"
/* Assume that myparser.h includes
* int parseFile(FILE* input);
* which returns non-zero on failure.
*/
int main(int argc, char* argv[]) {
FILE* input = stdin; /* If nothing changes, this is what we parse */
if (argc > 1) {
if (argc > 2) {
/* Too many arguments */
fprintf(stderr, "Usage: %s [FILE]\n", argv[0]);
exit(1);
}
/* The convention is that using `-` as a filename is the same as
* specifying stdin. Just in case it matters, follow the convention.
*/
if (strcmp(argv[1], "-") != 0) {
/* It's not -. Try to open the named file. */
input = fopen(argv[1], "r");
if (input == NULL) {
fprintf(stderr, "Could not open '%s': %s\n", argv[1], strerror(errno));
exit(1);
}
}
}
return parse(input);
}
It would probably have been better to have packaged most of the above into a function which takes a filename and returns an open FILE*.
I guess my brain is fried because this was a very basic question and I realized it right after I posted it. I will leave it up for others who might need it.
ANSWER:
You can fgets from stdin, then to check for the end of the file you can still use feof for stdin by using the following:
while(!feof(stdin))
I wrote a program that asks the user to enter the full pathname of a file. It will then attempt to open that file from the pathname string provided. I used the standard error checking that most books have recommended, which is to close the program if fopen() returns NULL (which it will do in the case that the file does not exist). When I run the program and enter some random characters when prompted (obviously not a valid filename) my program hangs with a runtime error because it's trying to open that file that doesn't exist.
What is the point of the standard error check (pfile == NULL) if your program has already crashed when it calls fopen()? See below code.
I'm using LabWindows CVI 2017 as my enfironment which uses the clang compiler. See image of run time error.
#include <stdio.h>
#include <string.h>
#define MAX 200
int main (void){
char buffer[MAX];
int len = 0;
FILE *pfile = NULL;
printf("please enter the full pathname of the file you wish to process.\n");
fgets(buffer, MAX, stdin);
len = strlen(buffer);
buffer[len - 1] = '\0';
pfile = fopen(buffer, "r");
if(pfile == NULL){
printf("not a valid filename, press any key to exit.");
getchar();
return -1;
}
int sum = 0;
int c = 0;
while((c = fgetc(pfile)) != EOF){
sum += sizeof(c);
}
printf("the size of your file is %d\n", sum);
getchar();
return 0;
}
You are doing the proper error handling. Your program is valid in that respect. However, your IDE does some extra error checking, which is the cause of the behavior you're seeing.
The usual rules for error checking in these sorts of situations are:
Do check for error returns. (You're doing that.)
Do print a useful error message. (You're doing that.)
Print error messages to stderr.
If the error involves a file, do include the filename in the error message.
If the error involves a function that sets errno, do print the "perror" text" ("No such file or directory", etc.).
If you're writing a tool that will be combined into larger scripts, do include the program's name in the error message.
If the error occurs due to an input file you're reading, do print the name of that file and the line number.
Adopting rules 1 through 6, an improved version of your error check would be
if(pfile == NULL) {
fprintf(stderr, "%s: can't open %s: %s\n", progname, buffer, strerror(errno));
return EXIT_FAILURE;
}
For this to work you'll need both of:
#include <string.h>
#include <errno.h>
If that's too much work, a simpler way is just to call
perror(buffer);
although this falls down somewhat on rules 2, 6, and 7.
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!
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
}