detect EOL in C using fgets - c

I'm attempting to read a single line from a file using the following...
while(fgets(c,BUF,f) != EOL){
puts(c);
}
Where EOL = #define EOL '\n' however, I get the warning... comparison between pointer and integer
What is the correct way to achieve what I'm trying?

fgets reads a string, and the result type is char*
I'd think you are thinking of fgetc instead?

Why don't you try fgetc instead? Then you can compare the ASCII code of '/n' like this
while (fgetc (file) != '\n'){
puts(c);
}

You need to dereference the returned char* and compare that to your EOL ... not compare the actual pointer address itself to the end-of-line character.
Change your code to this:
char* return_val = NULL;
while((return_val = fgets(c,BUF,f)) != NULL && *return_val != EOF)
{
puts(c);
}
if (retun_val == NULL)
{
//handle error
}

You have to examine the contents of c after the call to fgets to determine if a newline was included in the returned string:
for (;;) {
if (fgets(c, BUF, f) == NULL) { /* handle error */ }
if (strchr(c, EOL) != NULL) {
puts(c);
} else {break; }
}

fgets reads characters from the stream and writes them in the buffer until either the buffer is almost full or it finds a '\n' (it returns NULL if the operation fails).
So you can know how many characeters were read (including the '\n') with strlen ...
ret = fgets(buffer, sizeof buffer, stdin);
if (ret == NULL) /* deal with error */;
chars = strlen(buffer);
Now, one of 2 things may have hapenned: either a '\n' was read before the buffer got full or the buffer gor full before a '\n' was read. You can know which was it by examining the last character in the buffer
if (buffer[chars - 1] == '\n') /* full line read */;
else /* incomplete line */;
Note that chars is always (*) 1 or more, so the expression buffer[chars - 1] is ok.
(*) it could be 0 only with binary data for input, but that denies the use of strlen and other string functions.

Related

How to accept string input only if it of certain length in C else ask user to input the string again

How to accept set of strings as input in C and prompt the user again to re-enter the string if it exceeds certain length. I tried as below
#include<stdio.h>
int main()
{
char arr[10][25]; //maximum 10 strings can be taken as input of max length 25
for(int i=0;i<10;i=i+1)
{
printf("Enter string %d:",i+1);
fgets(arr[i],25,stdin);
}
}
But here fgets accepts the strings greater than that length too.
If the user hits return, the second string must be taken as input. I'm new to C
How to accept string input only if it of certain length
Form a helper function to handle the various edge cases.
Use fgets(), then drop the potential '\n' (which fgets() retains) and detect long inputs.
Some untested code to give OP an idea:
#include <assert.h>
#include <stdio.h>
// Pass in the max string _size_.
// Return NULL on end-of-file without input.
// Return NULL on input error.
// Otherwise return the buffer pointer.
char* getsizedline(size_t sz, char *buf, const char *reprompt) {
assert(sz > 0 && sz <= INT_MAX && buf != NULL); // #1
while (fgets(buf, (int) sz, stdin)) {
size_t len = strlen(buf);
// Lop off potential \n
if (len > 0 && buf[--len] == '\n') { // #2
buf[len] = '\0';
return buf;
}
// OK if next ends the line
int ch = fgetc(stdin);
if (ch == '\n' || feof(stdin)) { // #3
return buf;
}
// Consume rest of line;
while (ch != '\n' && ch != EOF) { // #4
ch = fgetc(stdin);
}
if (ch == EOF) { // #5
return NULL;
}
if (reprompt) {
fputs(reprompt, stdout);
}
}
return NULL;
}
Uncommon: reading null characters remains a TBD issue.
Details for OP who is a learner.
Some tests for sane input parameters. A size of zero does not allow for any input saved as a null character terminated string. Buffers could be larger than INT_MAX, but fgets() cannot directly handle that. Code could be amended to handle 0 and huge buffers, yet leave that for another day.
fgets() does not always read a '\n'. The buffer might get full first or the last line before end-of-file might lack a '\n'. Uncommonly a null character might be read - even the first character hence the len > 0 test, rendering strlen() insufficient to determine length of characters read. Code would need significant changes to accommodate determining the size if null character input needs detailed support.
If the prior fgets() filled its buffer and the next read character attempt resulted in an end-of-file or '\n', this test is true and is OK, so return success.
If the prior fgetc() resulted in an input error, this loops exits immediately. Otherwise, we need to consume the rest of the line looking for a '\n' or EOF (which might be due to an end-of-file or input error.)
If EOF returned (due to an end-of-file or input error), no reason to continue. Return NULL.
Usage
// fgets(arr[i],25,stdin);
if (getsizedline(arr[i], sizeof(arr[i]), "Too long, try again.\n") == NULL) {
break;
}
This code uses a buffer slightly larger than the required max length. If a text line and the newline can't be read into the buffer, it reads the rest of the line and discards it. If it can, it again discards if too long (or too short).
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define INPUTS 10
#define STRMAX 25
int main(void) {
char arr[INPUTS][STRMAX+1];
char buf[STRMAX+4];
for(int i = 0; i < INPUTS; i++) {
bool success = false;
while(!success) {
printf("Enter string %d: ", i + 1);
if(fgets(buf, sizeof buf, stdin) == NULL) {
exit(1); // or sth better
}
size_t index = strcspn(buf, "\n");
if(buf[index] == '\0') { // no newline found
// keep reading until end of line
while(fgets(buf, sizeof buf, stdin) != NULL) {
if(strchr(buf, '\n') != NULL) {
break;
}
}
if(feof(stdin)) {
exit(1); // or sth better
}
continue;
}
if(index < 1 || index > STRMAX) {
continue; // string is empty or too long
}
buf[index] = '\0'; // truncate newline
strcpy(arr[i], buf); // keep this OK string
success = true;
}
}
printf("Results:\n");
for(int i = 0; i < INPUTS; i++) {
printf("%s\n", arr[i]);
}
return 0;
}
The nice thing about fgets() is that it will place the line-terminating newline character ('\n') in the input buffer. All you have to do is look for it. If it is there, you got an entire line of input. If not, there is more to read.
The strategy then, is:
fgets( s, size_of_s, stdin );
char * p = strpbrk( s, "\r\n" );
if (p)
{
// end of line was found.
*p = '\0';
return s; (the complete line of input)
}
If p is NULL, then there is more work to do. Since you wish to simply ignore lines that are too long, that is the same as throwing away input. Do so with a simple loop:
int c;
do c = getchar(); while ((c != EOF) && (c != '\n'));
Streams are typically buffered behind the scenes, either by the C Library or by the OS (or both), but even if they aren’t this is not that much of an overhead. (Use a profiler before playing “I’m an optimizing compiler”. Don’t assume bad things about the C Library.)
Once you have tossed everything you didn’t want (to EOL), make sure your input isn’t at EOF and loop to ask the user to try again.
Putting it all together
char * prompt( const char * message, char * s, size_t n )
{
while (!feof( stdin ))
{
// Ask for input
printf( "%s", message );
fflush( stdout ); // This line _may_ be necessary.
// Attempt to get an entire line of input
if (!fgets( s, n, stdin )) break;
char * p = strpbrk( s, "\r\n" );
// Success: return that line (sans newline character(s)) to the user
if (p)
{
*p = '\0';
return s;
}
// Failure: discard the remainder of the line before trying again
int c;
do c = getchar(); while ((c != EOF) && (c != '\n'));
}
// If we get this far it is because we have
// reached EOF or some other input error occurred.
return NULL;
}
Now you can use this utility function easily enough:
char user_name[20]; // artificially small
if (!prompt( "What is your name (maximum 19 characters)? ", user_name, sizeof(user_name) ))
{
complain_and_quit();
// ...because input is dead in a way you likely cannot fix.
// Feel free to check ferror(stdin) and feof(stdin) for more info.
}
This little prompt function is just an example of the kinds of helper utility functions you can write. You can do things like have an additional prompt for when the user does not obey you:
What is your name? John Jacob Jingleheimer Schmidt
Alas, I am limited to 19 characters. Please try again:
What is your name? John Schmidt
Hello John Schmidt.

Buffer and its interaction with function?

I am facing some problems while understanding the following code.
It is a program to read Strings from keyboard if the length of the String is lesser than the specified size (i.e 'n' here).
If the length of a string is larger than the specified size, the remaining characters on the line will be discarded.
More specifically, I want to know what is happening inside the buffer and how getchar() is reading the data and not storing it in the buffer.
char * s_gets(char * st, int n)
{
char * ret_val;
int i = 0;
ret_val = fgets(st, n, stdin);
if (ret_val) // i.e., ret_val != NULL
{
while (st[i] != '\n' && st[i] != '\0')
i++;
if (st[i] == '\n')
st[i] = '\0';
else // must have words[i] == '\0'
while (getchar() != '\n')
continue;
}
return ret_val;
}
The code is slightly flawed, but does more or less the job outlined. It uses fgets() to do a lot of the work. That reads up to n - 1 characters from standard input. When it returns, there are a few possibilities:
(EOF) Nothing was available to be read. Nothing has been put in the buffer, but fgets() returned NULL.
(Normal) A line has been read and fitted into the buffer st. The buffer includes the newline.
(Overlong) Part of a line has been read, but the input did not find a newline.
(EOF without newline) Some data was read, but there wasn't a newline before EOF was detected.
Case 1 is simplest: the code returns NULL. Case 2 is handled by scanning the string that's read to find the newline. If the newline is found, it is overwritten with a null byte. Case 3 is handled at the same time; if the value found isn't a newline, it must be the null byte. The code drops into a loop that reads more characters until a newline is read. Case 4 is similar to case 3 in effect, but the code in the loop mishandles this — it doesn't detect and handle EOF, so the code would fall into an indefinite loop. That's a bug that needs to be fixed.
The getchar() loop doesn't assign anything to the buffer st — it makes no changes to st. That continues to contain a null terminated string as read by fgets(). The getchar() loop reads and discards any characters left on the line that was read that did not fit into the buffer.
The code should be:
char *s_gets(char *st, int n)
{
assert(n > 1 && st != NULL);
char *ret_val = fgets(st, n, stdin);
if (ret_val != NULL)
{
int i = 0;
while (st[i] != '\n' && st[i] != '\0')
i++;
if (st[i] == '\n')
st[i] = '\0';
else
{
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
}
}
return ret_val;
}
The return value of NULL or the original string is the same as fgets() uses, but it isn't the most useful return value. It would be more useful, most often, if the code returned the length of the string that it read, or returned EOF if it encountered EOF (or a read error). The information is readily available — in the variable i.

switching from getchar to fgets

I am trying to switching my use of getchar to fgets but, when using getchar, the entire code does not work.
//fgets(line, sizeof(line), stdin);
while(fgets(line, sizeof(line), stdin))
{
portNum[sizeof(line)] = (char)line;
}
while((c = getchar()) != '\n')
{
portNum[num++] = c;
}
portNum[num] = '\0';
How can I make equal for those two functions to work properly?
You usage of fgets is wrong.
fgets Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
In your case fgets will read all the characters until newline is encountered.
Also, the parameters usage is wrong.
char * fgets ( char * str, int num, FILE * stream );
str => Pointer to an array of chars where the string read is copied.
num => Maximum number of characters to be copied into str (including the
terminating null-character).
stream => Pointer to a FILE object that identifies an input stream.
stdin can be used as argument to read from the standard input.
Refer to the fgets documentation for more information.
fgets man page
OP's fgets() usage is unclear and portNum[sizeof(line)] = (char)line; is certainly in error.
Instead: how to make the below getchar() code more fgets()-like:
// assumed missing code
#define N 100
int c;
char portNum[N];
size_t num = 0;
// size and EOF detection added (which should have been there)
while(num + 1 < sizeof portnum && (c = getchar()) != '\n' && c != EOF) {
portNum[num++] = c;
}
portNum[num] = '\0';
// assumed missing code
if (c == EOF && num == 0) Handle_EndOfFile_or_InputError();
else ...
This can be replaced with fgets() code
#define N 100
char portNum[N+1]; // 1 larger for the \n
if (fgets(portNum, sizeof portNum, stdin)) {
// lop off potential trailing \n
portNum[strcspn(portNum, "\n")] = '\0';
...
} else {
Handle_EndOfFile_or_InputError();
}

How to properly use strcasecmp in c

I am reading one line from a file which contains on the first line the word "hello". And then I am comparing it with "hello" using strcasecmp, however it is telling me it is still different
char *line = NULL;
size_t len = 100;
printf("%s", argv[1]);
FILE * fp = fopen(argv[1], "r");
if (fp == NULL) {
printf("empty\n");
exit(0);
}
getline(&line, &len, fp);
if (strcasecmp(line, "hello") == 0) {
printf("same");
}
strcasecmp will only return 0 if the strings are the same (except for the case), not if the first string starts with the second string.
And getline reads the newline character at the end of the line, so if you type "hello" the string you get in "line" will be "hello\n".
this is from the man page for getline()
getline()
reads an entire line from stream, storing the address of the
buffer containing the text into *lineptr.
The buffer is null-terminated
and includes the newline character, if one was found.
Notice that part about including the newline character.
So, either limit the length of the comparison or better, trim the new line char, using something similar to:
char * newline = NULL;
if( NULL != (newline = strchr( line, '\n' ) )
{ // then newline found
*newline = '\0';
}

Good way to flush scanf buffer when invalid entry entered

I have been thinking of ways to flush bad entries in scanf functions to allow for loop prompts to do their job.
I have a function call here that flushes the input. This works but it's still buggy if I enter something like 2q when I ask for an int.
void flushKeyBoard()
{
int ch; //variable to read data into
while((ch = getc(stdin)) != EOF && ch != '\n');
}
And then I'll have something like this in another function:
printf("Enter a value: ");
check = scanf("%f", &b);
while(check == 0)
{
printf("Invalid number entered. Please re-enter: ");
check = scanf("%f, &b");
flushKeyBoard();
}
Any better ideas? Someone suggested fflush(); but it's not really standard to use that here..
Using getchar (common, easily comprehensible)
int c;
while ((c = getchar()) != EOF && c != '\n'); /* <note the semicolon! */
if (c == EOF) {
if (feof(stdin)) {
/* Handle stdin EOF. */
}
else {
/* Handle stdin error. */
}
}
Using fgets (less common, less comprehensible)
char buf[8];
while (fgets(buf, sizeof buf, stdin) != NULL) {
size_t len = strlen(buf);
/*
* Exit the loop if either EOF was encountered before '\n', or
* if '\n' is detected.
*/
if (len + 1 != sizeof(buf) || memchr(buf, '\n', len))
break;
}
if (feof(stdin)) {
/* Handle stdin EOF. */
}
else {
/* Handle stdin error. */
}
Using a scanset with scanf (likely uncommon, easily comprehensible)
/*
* Combining the scanset with assignment suppression (the '*' before the
* scanset) will return EOF on EOF/error and 0 if '\n' was read.
*/
if (scanf("%*[^\n]") == EOF) {
if (feof(stdin)) {
// Handle stdin EOF.
}
else {
// Handle stdin error.
}
}
getchar(); // Flush the '\n'.
Using getline (likely uncommon, difficult)
char *buf = NULL;
size_t bufsize = 0;
ssize_t len;
/* getline() will stop reading on '\n' or EOF. */
len = getline(&buf, &bufsize, stdin);
/* No bytes read and EOF encountered, or there was an error. */
if (len == -1) {
if (feof(stdin)) {
/* Handle stdin EOF. */
}
else if (ferror(stdin)) {
/* Handle stdin error. */
}
else {
/* Handle errno error, if desired. */
}
/*
* The value of "buf" is indeterminate here, so you likely
* just want to return from the function/program at this point
* rather than continuing and potentially freeing an invalid
* buffer.
*/
}
free(buf);
Of course, all of these methods assume you want to handle things that happen on EOF/error differently than with \n, perhaps even all three being separate cases. For example, by placing one of the above snippets into a self-contained function, you might return 0 if \n was read or EOF on EOF/error, or even 0 on \n, EOF on EOF, and 1 on error.
Things worth noting:
The getchar and fgets methods are 100% cross-platform. I prefer the getchar method for its simplicity.
The scanset method is less cross-platform only because not all compilers implement scansets (and are therefore not C99-compliant).
The getline method is also not quite cross-platform: it's primarily implemented on GNU/Linux and on some other POSIX operating systems; this does not include Windows at this time. It isn't terribly difficult to write one yourself once you have some experience managing memory and working with pointers, but you'd be better off using one of the first two methods since writing an implementation of getline would likely end up using fgetc or fgets anyway (fgetc(stdin) and getchar() should behave identically).
You can use:
fscanf(stdin, "%*[^\n]%*c");
There's a few ways of doing this. Effectively, what you're trying to do is consume a line of text up to a \n character (or an EOF.) To do this properly, you should use sscanf (or fscanf.)
sscanf("%*[^\n]\n");
This uses the regular expression [^\n]*\n (Any number of characters that are NOT \n, followed by a \n) and consumes everything matching that.
Edit: Because I'm dumb and forgot that scanf regex's use a slightly different syntax.

Resources