Clear buffer stdin - c

Hello Stackoverflow community, I have a problem with this code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_SIZE 50
int main()
{
char *s = malloc(sizeof(char)*MAX_SIZE);
do{
int r = read(STDIN_FILENO, s, MAX_SIZE);
if(r==-1) printf("error");
else{
write(STDERR_FILENO, s, MAX_SIZE);
}
while(getchar()!='\n');
} while(strcmp(s,"end\n")!=0);
free(s);
return 0;
}
The problem is that it produces me a wrong output, stdin doesn't results "clean" at each iteration of the 'do while'. I know that on windows i must use fflush(stdin) but watching on internet i saw that this function is not portable for linux. i use linux, and always watching on internet i saw that many people say the solution is while(getchar()!='\n); instead of fflush(stdin), but the problem is not resolved... Can you explain me why?

Problems that I see:
You are passing the wrong argument to write. Instead of
write(STDERR_FILENO, s, MAX_SIZE);
it should be
write(STDERR_FILENO, s, r); // Write the number of characters that were read
// not MAX_SIZE
The strategy to skip till the end of the line is not well thought out. You have:
int r = read(STDIN_FILENO, s, MAX_SIZE);
if(r==-1) printf("error");
else{
write(STDERR_FILENO, s, MAX_SIZE);
}
while(getchar()!='\n');
First of all, read will not stop reading when it encounters a newline. It will read up to MAX_SIZE characters. It will gobble up as many newlines as it can in the process. What you need to do is use fgets() instead.
char* cp = fgets(s, MAX_SIZE, stdin);
if ( cp == NULL )
{
// If fgets fails, you probably reached the end of the file.
break;
}
fprintf(stderr, "%s", s);
The line
while(strcmp(s,"end\n")!=0);
will lead to undefined behavior if you use read to read the data from a file since read does not automatically add a terminating null character to s. Using fgets avoids that problem.
Also, since you know the size of the array at compile time, you can use:
char s[MAX_SIZE];
instead of using malloc to allocate memory at run time.

FYI, an equivalent C++ program
#include <iostream>
#include <string>
int main() {
for (std::string line; std::getline(std::cin, line); ) {
if (line == "end")
break;
}
}

Related

C reading lines from stdin

My goal is to read every line from a piped .txt file with the getline() function, but I somehow get a error every time I use this function:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int Chars;
int size = 10;
char *string;
printf("Please enter a string: ");
string = (char*) malloc(size);
Chars = getline(&string, &size, stdin);
if (Chars == -1)
{
puts("ERROR!");
}
else
{
puts("You entered the following string: ");
puts(string);
printf("\nCurrent size for string block: %d", Chars);
}
return 0;
}
I always get the errorcode: [Error] Id retruned 1 exit status
I've reproduced the linking error on DevC++, in which getline() seems to be missing even after forcing recent C revisions with gcc compiler options such as -std=c11.
So I've rewritten your code using fgets():
char *fgets(char *s, int size, FILE *stream);
It is for sure more portable than getline but has a few differences:
It reads up to size-1 characters if the newline is not encountered before this limit (it automatically appends the string terminator). So it doesn't manage buffer reallocation
The resulting string contains the '\n' character, if found
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STR_SIZE 32
int main( void )
{
int len = 0;
char *str;
printf("Please enter a string: ");
str = malloc(MAX_STR_SIZE); /* (1) */
while( 1 )
{
size_t newline_pos;
fgets( str, MAX_STR_SIZE, stdin );
/* (2) */
if( len == 0) /* (3) */
{
puts("You entered the following string: ");
}
newline_pos = strcspn(str, "\n" );
str[newline_pos] = '\0';
len += strlen(str); /* (4) */
fputs(str, stdout);
if(newline_pos < MAX_STR_SIZE-1) /* (5) */
break;
}
printf("\n\nCurrent size for string block: %d", len);
free( str ); /* (6) */
return 0;
}
So, basically, I just use fgets to read from stdin, iterating until the '\n' character is found. In order to understand is this condition is met, I use strcspn() function, and I use the same function to remove the newline from the resulting string.
A few notes/assumptions (check the corresponding number in code section):
Casting the result of malloc is required only if you are compiling with a C++ compiler. It can be omitted in C
Removed fgets error check: it returns NULL in case of error (no chars read before EOF is found. It won't happen reading from stdin)
Checking for len==0 we make sure that the "You entered the following string: " is printed only once
The length of the string is calculated by summing the length of the strings read in every iteration
The break condition is met when the string contains '\n'. Otherwise strcspn's return value will be MAX_STR_SIZE
Even if the OS will release all the dynamic memory used by the program, on return, it is a good habit always freeing it anyway

How to read/write non ascii characters?

I am trying to input a file and have it be printed each character at a time but some characters are ignored.
I'm assuming that that's because they are non ascii chars and the fgets doesn't know what to do with them since the buffer is made of chars.
int main() {
while(1)
{
char str[50];
if (fgets(str, 50, stdin) == NULL)
{
exit(0);
}
for(int i = 0; str[i] != '\n' ; i++)
{
printf("%lc", str[i]);
}
printf("\n");
}
return 0;
}
I have a file with
ALICE’SE’E’E’E’E’E’
but my code outputs it as
ALICESEEEEEE
if you use fgets(), that will probably have some undefined behaviour with input characters like \0, as they are internally used by the string functions to mark the end of the data in a string.
fgets()is a text oriented function that reads input until it finds a new line character \n. It then places a \0 after it, so you know where the string ends.
But with binary data, you can get control characters, even null characters in the middle of the data stream, making sometimes characters to dissapear on output (because they have been read, but your code doesn't go further when some of these are encountered later on writing).
If you want to allow all binary characters, you have several approaches here:
Use the binary stream counterparts from stdio: fread(3) and fwrite(3) functions allow you to read binary data as well as text:
#include <stdio.h>
#include <stdlib.h> /* for EXIT_* constants and exit() */
#define N (8192) /* buffer size (guessed, probably not optimum) */
int main()
{
ssize_t n;
char buffer[N];
while((n = fread(buffer, sizeof buffer[0], N, stdin)) > 0) {
ssize_t nout = fwrite(buffer, sizeof buffer[i], n, stdout);
if (nout != n) { /* error */
fprintf(stderr, "Error writing stdout\n");
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
} /* main */
Use the simple Kernighan & Ritchie's sample from "The C programming language" book (I've added some error processing code):
#include <stdio.h>
#include <stdlib.h> /* idem. */
int main()
{
while((c = fgetc(stdin)) != EOF)
if (fputc(stdout) == EOF) {
fprintf("fputc error\n");
exit(EXIT_FAILURE);
}
}
if (ferror(stdin)) {
fprintf("fgetc error\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} /* main */
or use the standard UNIX system calls:
#include <unistd.h> /* for prototypes for read()/write() syscalls */
#include <stdio.h>
#include <stdlib.h>
#define N (8192) /* guessed buffer size */
int main()
{
char buffer[N];
ssize_t n;
while ((n = read(0, buffer, sizeof buffer)) > 0) {
ssize_t nout;
nout = write(1, buffer, sizeof buffer));
if (nout != n) {
fprintf(stderr, "write: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
}
if (n < 0) {
fprintf(stderr, "read: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} /* main */
but beware that probably the most efficient code you'll get can be the original character oriented sample from the K&R book, as stdio will select an optimum buffer size that will make it run faster, despite of the higher number of loop executions.
note
Anyway, your output will be far to be what you want, as some control characters are not output to the terminal, but interpreted as control characters (most popular being \n, which makes the terminal to continue on the next line) You have also to deal with this.
Even if you read multibyte characters as single byte, you can process those with the examples given, as a character that uses two bytes, will be read as two, but on printing, those will become the single char the terminal should display. As long as you apply no transformation to the data flow, there will be no difference in output with the sample code snippets you have above this.
You are using fgets, which deals with chars, and a char array (char str[50]).
But a printf formatter %lc which is for wide char.
If you want to input wide char, you need to use fgetws, and an array of type( wchar_t str[50])

Extreme troubles with full line input. C Programming Language

I am having the absolute craziest time getting full line input to work. I will explain my problem. I need to get a full line of input, including a space, from the user entered at the keyboard. Simple right? Wrong!
MY GOAL
Store multiple strings, with spaces, into variables. If it makes a difference, I want to make the variables equal to a char pointer. So once I get the input from tempString, I want to set it to a char pointer. Like so:
char *variable1, *variable2;
//get user input
variable1 = tempString;
//get more user input
variable 2 = tempString;
//etc etc etc
Here's what I've tried.
First try
char tempString[100];
scanf("%s", &tempString);
printf("%s", tempString);
Invalid: scanf will stop reading at a white space, so "Example String" would just end up being "Example".
Second try
So I do more research. I thought I found the magic fix.
char tempSTring[100];
fgets(tempString, 100, stdin);
printf("%s", tempString);
Originally this works. However there is a massive problem. I need to get the user to enter about 8 inputs. Meaning I have to use a command like this 8 times. The problem is the program often skips over the fgets command. If I use a scanf previously, somehow the \n character is stuck in the input stream, and automatically feeds into fgets, satisfying its stdin input, and then does not prompt the user for input.
Third try
After thinking fgets was maybe my solution with a work around, I tried some tricks.
char tempSTring[100];
getc(stdin);
fgets(tempString, 100, stdin);
printf("%s", tempString);
I tried adding this getc(stdin) line. It worked for much of my program. It absorbs the \n character left behind in the stream. When it does so, great, it works. But sometimes, for some reason, the \n is NOT left in the stream, and when debugging, it looks like getc(stdin) is requesting input from the user, so it pauses my program to ask for input.
Question
These don't work for me.
How should I be doing this easy task?
To read (up to) 8 lines from a file, you can use either of these solutions. I decline to use variables char *variable1, *variable2, …; — that is an array seeking to escape.
POSIX getline()
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
enum { MAX_LINES = 8 };
char *lines[MAX_LINES];
int index = 0;
char *buffer = 0;
size_t buflen = 0;
while (index < MAX_LINES && getline(&buffer, &buflen, stdin) != -1)
{
lines[index++] = buffer;
buffer = 0;
buflen = 0;
}
free(buffer); // Space may be allocated before EOF is detected
for (int i = 0; i < index; i++)
printf("%d: %s", i, lines[i]);
return 0;
}
If getline() fails to allocate memory, it will report an error, so there is no need to do an explicit error check.
Standard C fgets()
Code using strdup(), another POSIX function. It isn't a part of standard C (though it is widely available). It is trivial to implement.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
enum { MAX_LINES = 8 };
char *lines[MAX_LINES];
int index = 0;
char buffer[4096];
while (index < MAX_LINES && fgets(buffer, sizeof(buffer), stdin) != 0)
{
if ((lines[index] = strdup(buffer)) == 0)
break;
index++;
}
for (int i = 0; i < index; i++)
printf("%d: %s", i, lines[i]);
return 0;
}
The test in the loop allows for the possibility of strdup() failing to allocate memory.
Notes
Both the solutions above keep the newline at the end of the input string. If you don't want that, you can zap it with:
lines[i][strcspn(lines[i], "\r\n")] = '\0';
This overwrites a carriage return or newline with a null byte, transforming DOS or Unix line endings. You then need to adjust the printing which assumes the string includes a newline. Note that the expression shown works correctly even if there is no carriage return or newline in the string.
The fgets() solution will break lines at 4095 characters, leaving the rest to be read as 'the next line'. If that's not acceptable, you have a variety of strategies open to you.
You can detect whether there is a newline and arrange to allocate more memory and read the next section of the line into the extra memory, repeating until you come across a newline or EOF.
You can read the remaining characters up to the newline or EOF:
int c;
while ((c = getchar()) != EOF && c != '\n')
;
Implementing strdup()
If for some reason your system doesn't have an implementation of strdup(), you can create a surrogate with:
#include <assert.h>
#include <stdlib.h>
#include <string.h>
char *strdup(const char *old_str)
{
assert(old_str != 0);
size_t old_len = strlen(old_str) + 1;
char *new_str = malloc(old_len);
if (new_str != 0)
memmove(new_str, old_str, old_len);
return new_str;
}
Here's how we old fart C programmers would do it:
#include <stdio.h>
#define MAX_LEN 100
int main( )
{
int c;
char input[MAX_LEN+1];
int i = 0;
while ( (c=getchar()) != '\n' && c != EOF && i < MAX_LEN)
input[i++] = c;
if (c == EOF || c =='\n') {
/* received input that terminated within buffer limit */
input[i] = '\0';
printf("read in your input string of: %s\n", input);
}
else {
printf("don't buffer overflow me dude!\n");
return -1;
}
return 0;
}
But nowadays people will tell you to use one of the library functions. I'm still an old fart though.
EDIT: Fixed my embarrassing mistakes pointed out by the helpful comments below.
You can take care of '\n' left by previous scanf by writing it like this -
scanf("%d%*c", &x); //<-- example to take int input
%*c will read from stdin and then discard it, thus '\n' would be removed from stdin.
You can achieve with scanf like this (a way for your previous attempt)-
char tempString[100];
/* As suggested by chqrile it is essential to check return of scanf */
if(scanf("%99[^\n]", tempString)!=1){
// ^^ & not required
tempString[0]='\0';
}
%99[^\n] this will read 99 characters and will stop only after encountering '\n' , thus would read input with spaces.

C Dynamic Array based on input from console

I am writing a program that stores input from the console. To simplify it lets say I need to output what was wrote to the console.
So I have something like this:
int main()
{
char* input;
printf("Please write a bunch of stuff"); // More or less.
fgets() // Stores the input to the console in the input char*
printf(input);
}
So that is it in more or less. Just trying to give you the general idea. So what if they input something the size of 999999999999. How can I assign a char* to be that size dynamically.
#include <stdio.h>
int main(void)
{
char input[8192];
printf("Please type a bunch of stuff: ");
if (fgets(input, sizeof(input), fp) != 0)
printf("%s", input);
return(0);
}
That allows for a rather large space. You could check that you actually got a newline in the data.
If that's not sufficient, then investigate the POSIX 2008 function getline(), available in Linux, which dynamically allocates memory as necessary.
Here's an example - you need to validate the input and make sure you don't overflow your buffer. In this example, I discard anything over the max length and instruct the user to try again. Another approach would be allocating a new (larger) buffer when that happened.
fgets() second argument is the maximum number of characters you will read from the input. I'm actually accounting for the \n in this example and getting rid of it, you may not want to do so.
#include <stdio.h>
#include <string.h>
void getInput(char *question, char *inputBuffer, int bufferLength)
{
printf("%s (Max %d characters)\n", question, bufferLength - 1);
fgets(inputBuffer, bufferLength, stdin);
if (inputBuffer[strlen(inputBuffer) -1] != '\n')
{
int dropped = 0;
while (fgetc(stdin) != '\n')
dropped++;
if (dropped > 0) // if they input exactly (bufferLength - 1) characters, there's only the \n to chop off
{
printf("Woah there partner, your input was over the limit by %d characters, try again!\n", dropped );
getInput(question, inputBuffer, bufferLength);
}
}
else
{
inputBuffer[strlen(inputBuffer) -1] = '\0';
}
}
int main()
{
char inputBuffer[10];
getInput("Go ahead and enter some stuff:", inputBuffer, 10);
printf("Okay, I got: %s\n",inputBuffer);
return(0);
}

Help with scanf behaving differently on Big Endian system

My code is supposed to read a line from the user, if the line starts with "output" then it prints out "Line is output" and waits for the user to enter another line.
If the line starts with "input" it prints out "Line is input" and terminates.
My code works fine on an Intel PC, however on a Debian SPARC it seems the scanf doesnt wait for input after the first time and just reads in an empty line or something infinitely.
Where am I going wrong here?
#include <stdio.h>
#include <string.h>
int main()
{
char buf[9000];
char key[5];
char *p=buf;
int readMore=1;
while(readMore)
{
//read in one line from stdin into buffer
scanf("%[^\n]",buf);
fflush(stdin);
sscanf(p, "%s",key); //get key from buffer
printf("Key:%s\n",key); //print key
if (strcmp("output",key)==0)
{
printf("Line is output\n");
}
if (strcmp("input",key)==0)
{
readMore=0;
printf("Line is input\n");
fflush(stdin);
getchar();
return 0;
}
key[0]=0;
buf[0]=0;
} //end while
return 0;
}
Fixed like this:
......
int bytes_read;
int nbytes = 100;
while(readMore)
{
/* These 2 lines are the heart of the program. */
p = (char *) malloc (nbytes + 1);
bytes_read = getline (&p, &nbytes, stdin);
....
This is not an endian issue. This is about how buffering of standard input is performed on the different platforms. Basically, you can't use fflush() on standard input (or any other input stream) - the C Standard says that doing so is undefined.

Resources