strchr() finding '\n' where there seemingly is none - c

I'm using a modified fgets() function called s_gets() that removes a newline from input or discards any of the remaining characters in the input buffer. It looks like the following;
char *s_gets(char *str, int n, FILE *pf) {
char *ret_val;
char *find;
ret_val = fgets(str, n, pf);
if (ret_val) {
find = strchr(str, '\n');
if (find) {
puts("Newline was found.");
printf("Character before \\n is %c\n", *(find - 1));
*find = '\0';
} else {
while (getchar() != '\n')
continue;
}
}
return ret_val;
}
When I use this function and pass it a FILE* to a file containing just the string apple on a single line, the puts() inside the if clause runs and the printf() statement prints Character before \n is e. My question is where is this mysterious newline coming from? Does this have anything to do with EOF? I'm compiling this with Apple LLVM version 10.0.0 (clang-1000.10.44.2) on macOS 10.14.

Even if the string "apple" is written on a single line, a newline character is automatically added to the end of that line by the editor (gedit for example). That's why you see it.
PS: As rici mentioned: Why should text files end with a newline?

Related

Avoiding adding a new line at the end of file

Given the code reading from the standard input
int main() {
int c;
while ((c = getchar()) != EOF) {
fprintf(stdout, "%c", c);
}
}
This code is fine for reading all contents from the stdin containing multiple lines. But it will add a new line at the end of file. How can I modify the above code so that I can prevent from adding an extra new line \n in the last line of stdin? The example of stdin is given below.
hello world!!!
how is going today?
this is the last line in stdin
As #NateEldredge said in a friendly way, removing the trailing '\n' from the last line is dumb. By convention, on UNIX-like systems, every line in a textfile must be terminated with '\n'. But if you actually want to remove the last newline, maybe to be compatible with some lesser OS, you have to delay printing of characters until you know if the next read returned EOF, or not:
#include <stdio.h>
int main(void)
{
int c = getchar();
int peek_c;
if (c != EOF)
{
/* Print out everything except the last char */
while ((peek_c = getchar()) != EOF)
{
fprintf(stdout, "%c", c);
c = peek_c;
}
/* If the last char was not '\n', we print it
(We only want to strip the last char if it is a newline) */
if (c != '\n')
fprintf(stdout, "%c", c);
}
}

Printing char of a file with fgetc() ending with a "?" symbol

I'm trying to print the chars (with fgetc) in a file one by a one with a while loop.
I'm using the latest Atom editor to write the code, and I compile with the GPP Compiler, by pressing F5 and the output is displayed in the xterm terminal.
int main(int argc, char const *argv[])
{
FILE* file = NULL;
file = fopen("text.txt", "r+");
int letter = 0;
if (file != NULL)
{
while(letter != EOF)
{
letter = fgetc(file);
printf("%c", letter);
}
I expected the output to be the text in my file, which it is, but at the end there's a question mark symbol.
What I understood after doing some research is that my fgetc function reads the EOF like a normal character and prints it, resulting in a question mark symbol at the end.
Thanks for your help !
... ending with a “?” symbol
doing
while(letter != EOF)
{
letter = fgetc(file);
printf("%c", letter);
}
you print letter before to check if it is EOF, so you (try to) print EOF which is not a character, producing the unexpected output
Example of a valid code :
while ((letter = fgetc(file)) != EOF)
putchar(letter); /* or printf("%c", letter); if you prefer */
I have been having the same issue and I found out that the return type for fgetc is an integer and it may be returning -1.

Insert a certain number of characters with spaces included

What I'm trying to accomplish is to take no more than "x" characters (spaces included) as input. I only know how to do both of them separately with scanf,
like the following:
scanf("%20s",str)
This takes no more than 20 characters.
scanf("%[^\n]s",str) takes spaces as well, but it has no limit.
I tried getline but it takes the \n as a value in the string as well and I don't want that. I hope I was clear enough about what I'm asking.
From what #chqrlie has told me I wrote this fuction:
void getstring(char *str, int len)
{
do
{
if (fgets(str, len, stdin))
{
fflush(stdin);
// if is not the first character to be the new line then change it to '\0' which is the end of the string.
if (str[0] != '\n')
str[strcspn(str, "\n")] = '\0';
}
}while (str[0] == '\n'); // Check if the user has inserted a new line as first character
}
The format for character classes does not have a trailing s, it is written this way:
scanf("%[^\n]", str)
If you wish to limit the maximum number of characters stored into the destination array, specify this number between the % and the [:
scanf("%20[^\n]", str)
Note however that the conversion will fail and scanf() will return 0 if there is an empty line pending for this conversion specification.
It is a common mistake to omit the test on the return value of scanf(), causing undefined behavior in case of conversion failures because the destination variables are left in their previous state (uninitialized in many cases).
It may be more effective to use fgets() and remove the trailing newline this way:
if (fgets(s, 20, stdin)) {
/* line was read, can be an empty line */
s[strcspn(s, "\n")] = '\0'; /* remove the trailing newline if any */
...
} else {
/* fgets() failed, either at end-of-file or because of I/O error */
...
}
You can use the following:
for(i = 0; i < x; i++)
{
getchar(c);
if(c == '\n') break;
str[i] = c;
}
But you must have to be aware of the existing newlines in the buffer. :)

How to scan a string with white spaces in structure members in C?

struct Demo{
char a[50];
char b[50];
int a;
};
Can anyone give the code for this structure Demo where a and b will contains string with different words[white-spaces].
I tried
scanf("[^\n]s",name.a); //where name is the object
fgets(name.a,50,stdin);
Note : we can't use gets method as well
So, If any other method is there, please provide me.
To read a line of user input into char a[50]; with its potential trailing '\n' trimmed:
if (fgets(name.a, sizeof name.a, stdin)) {
name.a[strcspn(name.a, "\n")] = '\0'; // trim \n
}
Work is needed to cope with consuming excessive long input lines and using the last element of name.a[] such as:
// Alternative
if (scanf("%49[^\n]", name.a) == 1) {
// consume trailing input
int ch;
while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
;
}
} else { // Handle lines of only \n, end-of-file or input error
name.a[0] = '\0';
}
The scanf("%49[^\n]%*c", name.a) approach has trouble in 2 cases:
1) The input is only "\n", nothing is saved in name.a and '\n' remains in stdin.
2) With input longer than 49 characters (aside from the '\n'), the %*c consumes an extra character, yet the rest of the long input line remains in stdin.
Both of these issues can be solves with additional code too.

How to take a line input in C?

I was trying to take a full line input in C. Initially I did,
char line[100] // assume no line is longer than 100 letters.
scanf("%s", line);
Ignoring security flaws and buffer overflows, I knew this could never take more than a word input. I modified it again,
scanf("[^\n]", line);
This, of course, couldn't take more than a line of input. The following code, however was running into infinite loop,
while(fscanf(stdin, "%[^\n]", line) != EOF)
{
printf("%s\n", line);
}
This was because, the \n was never consumed, and would repeatedly stop at the same point and had the same value in line. So I rewrote the code as,
while(fscanf(stdin, "%[^\n]\n", line) != EOF)
{
printf("%s\n", line);
}
This code worked impeccably(or so I thought), for input from a file. But for input from stdin, this produced cryptic, weird, inarticulate behavior. Only after second line was input, the first line would print. I'm unable to understand what is really happening.
All I am doing is this. Note down the string until you encounter a \n, store it in line and then consume the \n from the input buffer. Now print this line and get ready for next line from the input. Or am I being misled?
At the time of posting this question however, I found a better alternative,
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
{
printf("%s\n", line);
}
This works flawlessly for all cases. But my question still remains. How come this code,
while(fscanf(stdin, "%[^\n]\n", line) != EOF)
{
printf("%s\n", line);
}
worked for inputs from file, but is causing issues for input from standard input?
Use fgets(). #FredK
char buf[N];
while (fgets(buf, sizeof buf, stdin)) {
// crop potential \n if desired.
buf[strcspn(buf, "\n")] = '\0';
...
}
There are to many issues trying to use scanf() for user input that render it prone to mis-use or code attacks.
// Leaves trailing \n in stdin
scanf("%[^\n]", line)
// Does nothing if line begins with \n. \n remains in stdin
// As return value not checked, use of line may be UB.
// If some text read, consumes \n and then all following whitespace: ' ' \n \t etc.
// Then does not return until a non-white-space is entered.
// As stdin is usually buffered, this implies 2 lines of user input.
// Fails to limit input.
scanf("%[^\n]\n", line)
// Does nothing if line begins with \n. \n remains in stdin
// Consumes 1 char after `line`, even if next character is not a \n
scanf("%99[^\n]%*c", line)
Check against EOF is usual the wrong check. #Weather Vane The following, when \n is first entered, returns 0 as line is not populated. As 0 != EOF, code goes on to use an uninitialized line leading to UB.
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
Consider entering "1234\n" to the following. Likely infinite loop as first fscanf() read "123", tosses the "4" and the next fscanf() call gets stuck on \n.
while(fscanf(stdin, "%3[^\n]%*c", line) != EOF)
When checking the results of *scanf(), check against what you want, not against one of the values you do not want. (But even the following has other troubles)
while(fscanf(stdin, "%[^\n]%*c", line) == 1)
About the closest scanf() to read a line:
char buf[100];
buf[0] = 0;
int cnt = scanf("%99[^\n]", buf);
if (cnt == EOF) Handle_EndOfFile();
// Consume \n if next stdin char is a \n
scanf("%*1[\n]");
// Use buf;
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
worked for inputs from file, but is causing issues for input from standard input?
Posting sample code and input/data file would be useful. With modest amount of code posted, some potential reasons.
line overrun is UB
Input begins with \n leading to UB
File or stdin not both opened in same mode. \r not translated in one.
Note: The following fails when a line is 100 characters. So meeting the assumption cal still lead to UB.
char line[100] // assume no line is longer than 100 letters.
scanf("%s", line);
Personally, I think fgets() is badly designed. When I read a line, I want to read it in whole regardless of its length (except filling up all RAM). fgets() can't do that in one go. If there is a long line, you have to manually run it multiple times until it reaches the newline. The glibc-specific getline() is more convenient in this regard. Here is a function that mimics GNU's getline():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
long my_getline(char **buf, long *m_buf, FILE *fp)
{
long tot = 0, max = 0;
char *p;
if (*m_buf == 0) { // empty buffer; allocate
*m_buf = 16; // initial size; could be larger
*buf = (char*)malloc(*m_buf); // FIXME: check NULL
}
for (p = *buf, max = *m_buf;;) {
long l, old_m;
if (fgets(p, max, fp) == NULL)
return tot? tot : EOF; // reach end-of-file
for (l = 0; l < max; ++l)
if (p[l] == '\n') break;
if (l < max) { // a complete line
tot += l, p[l] = 0;
break;
}
old_m = *m_buf;
*m_buf <<= 1; // incomplete line; double the buffer
*buf = (char*)realloc(*buf, *m_buf); // check NULL
max = (*m_buf) - old_m;
p = (*buf) + old_m - 1; // point to the end of partial line
}
return tot;
}
int main(int argc, char *argv[])
{
long l, m_buf = 0;
char *buf = 0;
while ((l = my_getline(&buf, &m_buf, stdin)) != EOF)
puts(buf);
free(buf);
return 0;
}
I usually use my own readline() function. I wrote this my_getline() a moment ago. It has not been thoroughly tested. Please use with caution.

Resources