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);
}
}
Related
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?
I wrote a short program to test reading text files from stdin:
int main(){
char c;
while(!feof(stdin)){
c = getchar(); //on last iteration, this returns '\n'
if(!isspace(c)) //so this is false
putchar(c);
//remove spaces
while (!feof(stdin) && isspace(c)){ //and this is true
c = getchar(); // <-- stops here after last \n
if(!isspace(c)){
ungetc(c, stdin);
putchar('\n');
}
}
}
return 0;
}
I then pass it a small text file:
jimmy 8
phil 6
joey 7
with the last line (joey 7) terminated with a \n character.
My problem is, after it reads and prints the last line, then loops back to check for more input, there are no more characters to read and it just stops at the line noted in the code block.
Question: The only way for feof() to return true is after a failed read as noted here: Detecting EOF in C. Why isn't the final call to getchar triggering EOF and how can I better handle this event?
There are multiple problems in your code:
You do not include <stdio.h>, nor <ctype.h>, or at least you did not post the whole source code.
You use feof() to check for end of file. This is almost never the right method, as underscored in Why is “while ( !feof (file) )” always wrong?
You read the byte from the stream in a char variable. This prevents proper testing for EOF and also causes undefined behavior for isspace(c). Change the type to int.
Here is an improved version:
#include <stdio.h>
int main(void) {
int c;
while ((c = getchar()) != EOF) {
if (!isspace(c)) {
putchar(c);
} else {
//remove spaces
while ((c = getchar()) != EOF && isspace(c)) {
continue; // just ignore extra spaces
}
putchar('\n');
if (c == EOF)
break;
ungetc(c, stdin);
}
}
return 0;
}
While your method with ungetc() is functionally correct, it would be better to use an auxiliary variable this way:
#include <stdio.h>
#include <ctype.h>
int main(void) {
int c, last;
for (last = '\n'; ((c = getchar()) != EOF; last = c) {
if (!isspace(c)) {
putchar(c);
} else
if (!isspace(last))
putchar('\n');
}
}
return 0;
}
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.
I know I can get the first character of a line of standard input by using getchar(), but I only want the first character of each line. Is there a function I can use to get rid of the rest of the string entered into standard input (if it is more than one character)? if not, what methodology should I consider using to get rid of the rest of the standard input line?
char buf[100];
while(fgets(buf,sizeof(buf),stdin) != NULL)
{
if(strlen(buf)>0)
buf[1] = '\0';
printf("%s",buf);
}
Read the whole line using fgets() and just nul terminate it after the first character.
#include <stdio.h>
int main(void)
{
int ch;
size_t len;
for (len = 0; 1; ) {
ch = getc(stdin);
if (ch == EOF) break;
if (!len++) putc(ch, stdout); /* the first character on a line */
if (ch == '\n') len = 0; /* the line has ended */
}
return 0;
}
Please note that the first character on a line can actually be a '\n' !!!
// Get the character you need
char c = getchar();
// Skip the rest
int a;
while((a = getchar()) != '\n' && a != EOF);
If you know how many lines you'll have, you can put it in a loop.
I'm trying to read the first character of a file and whenever it's equal to '(' I should skip that line else get the first character from that line. I'm under a mac and I can make use of fgetln.
FILE *file = fopen("test.txt", "r");
char c;
while(fscanf(file, "%s", &c) != EOF) {
if (c != '(')
printf("%c", c);
}
That's my current code. I don't know how to skip lines, although I've tried to get the whole line and checked only the first char solving the skip problem. However this is not working I'm getting strange characters in my console instead of the ones inside test.txt. How should I do that?
The problem with using %s format specifier of fscanf is that is splits on spaces, not only on end-of-line characters. Moreover, reading it in a single-character buffer will nearly always produce undefined behavior.
There are several ways to solve this problem, using different APIs:
You could replace %s with %200[^\n], and passing a 201-character buffer instead of c,
Using fgets with a properly-sized buffer, and picking the initial character, or
Using a character-based API, and setting a "take next" flag each time that you see a '\n' character:
Here is how you can implement the third approach:
bool takeNext = true;
int ch;
while ((ch = fgetc(file)) != EOF) {
if (takeNext && ch != '(') {
printf("%c", ch);
}
takeNext = (ch == '\n');
}
Here is a slightly longer character-based approach, which conditions on whether the first character in a line is ( or not.
If it is (, then we consume everything up to and including the next newline without outputting.
If it not, then we do the same thing but we output the characters as we read them.
#include <stdio.h>
int main(){
FILE *file = fopen("test.txt", "r");
int c;
while((c = getc(file)) != -1) {
if (c == '(') {
// Skip until the next newline
do {
c = getc(file);
} while (c != -1 && c != '\n');
continue;
}
else {
putchar(c);
do {
c = getc(file);
putchar(c);
} while (c != -1 && c != '\n');
}
}
fclose(file);
}
Change c to string because fscanf reads string. See if the 1st character of c matches with (.
If it does not then print the line else skip the line.
FILE *file = fopen("test.txt", "r");
char c[100];
while(fscanf(file, "%s", c)) {
if (c[0] != '(')
printf("%s", c);
}
Use fgets to read whole lines. It is also safer than fscanf as it limits the reading to the buffer size.
To check if the first char is '(' you can refer to it directly:
if (buf[0]=='(')
or
if (*buf=='(')