My code goes something like this:
char k[1000];
while(1){
scanf("%s",&k);
if(k[0] == '\n'){
exit(0);}
/* Do some processing on k */
memset(k,0,1000);
}
My intention is to process user input per normal and terminate when user inputs empty string or new line. This doesn't seem to work.
Could you guys help on what went wrong?
On related note, I also want to terminate if it is the end of file, how should I do it for EoF?
Thank you in advance for all the help.
First off -- don't use scanf for user input. It is a minefield of subtle issues just waiting to bite new C programmers, instead use a line-oriented input function like fgets or POSIX getline. Both read up to (and including) the trailing '\n' every time (as long as you provide a buffer of sufficient size for fgets -- otherwise it just keep reading blocks of characters of its buffer size until it encounters a '\n' or EOF)
So to read user input until an empty-string or EOF is encountered, you could simply do something like the following:
#include <stdio.h>
#include <string.h>
#define MAXC 1000
int main (void) {
char k[MAXC] = "";
for (;;) { /* loop until empty-string of EOF */
printf ("input: "); /* prompt for input */
if (fgets (k, MAXC, stdin)) { /* read line (MAXC chars max) */
if (*k == '\n') { /* test for empty-string */
fprintf (stderr, "empty-string! bye.\n");
break;
}
size_t l = strlen (k); /* get length of string */
if (l && k[l - 1] == '\n') /* check if last char is '\n' */
k[--l] = 0; /* overwrite with nul-terminator */
printf ("got input: %s\n", k);
}
else { /* got EOF */
fprintf (stderr, "EOF -- bye.\n");
break;
}
}
return 0;
}
Example Use/Output
>bin\fgets_user_input.exe
input: this
got input: this
input: is some
got input: is some
input: input
got input: input
input:
empty-string! bye.
>bin\fgets_user_input.exe
input: this is more
got input: this is more
input: ^Z
EOF -- bye.
>bin\fgets_user_input_cl.exe
input: it works the same
got input: it works the same
input: compiled by gcc
got input: compiled by gcc
input: or by cl.exe (VS)
got input: or by cl.exe (VS)
input:
empty-string! bye.
(note: for Linux Ctrl+d generates the EOF, I just happened to be on windoze above)
Like ever so often, the problem here is inappropriate usage of scanf(). scanf() is not for reading input but for parsing it and the format-string tells it how to parse.
In your case, %s is looking for a sequence of non-whitespace characters (IOW, a word) and it skips any leading whitespace. \n (newline) is just a whitespace character, so it is always skipped -- your scanf() will just wait for more input until it can parse %s.
For more information on scanf() pitfalls, I recommend you my beginners' guide away from scanf(). As a rule of thumb, with interactive input (which is the default), scanf() is almost always wrong.
There's another huge problem with scanf("%s", ...): It will happily overflow any buffer you provide it, as long as the input contains non-whitespace characters, just like gets() which was even removed from C for exactly that reason: Buffer overflows are extremely dangerous! Therefore always use a field-width, in your case scanf("%999s", ...). This parses a maximum of 999 characters, leaving one for the necessary 0 byte terminating a string.
But now for how to do it correctly: There are several functions in C that are indeed for reading input and one of them is for reading a line of input: fgets(). In your code, it would look like this:
char k[1000];
while(fgets(k, 1000, stdin)){
if(k[0] == '\n'){
exit(0);
}
/* Do some processing on k */
memset(k,0,1000);
}
I used your original code here, still some further remarks:
It would be better to define a macro instead of using the magic number 1000, e.g. #define INPUTSIZE 1000 and use this instead, like char k[INPUTSIZE];, fgets(k, INPUTSIZE, stdin) etc.
Clearing the whole array is not needed, so to avoid unnecessary work, replace the memset() with just k[0] = '\0'; or similar. A C string ends at the first 0 byte, so this is enough to make k hold an empty string. If your program does nothing more than shown here, you could even get rid of this completely, as the next fgets() call overwrites the array anyways (or returns NULL on error, which would stop the loop).
Also note that fgets() reads the whole line including the newline character at the end, so keep this in mind when processing the contents of k.
This one is guaranteed to give everything except newlines (and EOFs) for you:
char k[1000];
scanf("%[^\n]", k);
And when it returns, the next character is guaranteed to be either a newline, or non-existent at all (EOF reached). Get it like this:
int next_char = getcgar();
if (next_char == EOF){
your_eof_process();
}
else if (nexr_char == '\n'){
your_newline_process();
}
Personally, I would do it using only getchar():
char k[1000];
int ind, tempc;
for (ind = 0; ind < sizeof k; ind ++){
tempc = getchar();
if (tempc == '\n'){
// Some stuff
}
else if (tempc == EOF){
// Other stuff
}
else {
k[ind] = tempc;
}
}
k[sizeof(k)-1] = '\0';
Related
I want to input a string with spaces and print that string with a another string on the same line.
int i = 4;
double d = 4.0;
char s[] = "Apple ";
int x;
double y;
char z[105];
scanf("%d",&x);
scanf("%lf",&y);
scanf("%[^105\n]s",z);
printf("%d\n",i+x);
printf("%0.1lf\n",d+y);
printf("%s %s",s,z);
return 0;
You scanf format specifier "%[^105]s" uses a character class [...] which is a stand-alone specifier in and of itself and does not requires 's' at the end. By placing 's' at the end you are forcing scanf to look for a literal 's' following an unlimited number of characters NOT including 1, 0, 5.
It appears you intended to use the number to protect your arrays bounds -- which is a good thing, but the proper format in that case is "%104[^\n]" which will read up to 104 characters that do not include a '\n' (preserving space for the nul-character).
For example:
if (scanf("%104[^\n]",z) == 1)
printf("%s %s\n",s,z);
(note: ALWAYS validate ALL user-input by at minimum checking the return)
Also note: by NOT reading the '\n' above, it is left in your input buffer (stdin) unread, and if your next attempted input is "%c" or "%[...]", you will take the '\n' as part of your input as nether "%c" or `"%[...]" consume leading whitespace.
Putting it together in an example you could do:
#include <stdio.h>
int main (void) {
char s[] = "Apple";
char z[105];
printf ("enter z: ");
if (scanf("%104[^\n]",z) == 1)
printf("%s %s\n",s,z);
else
fputs ("error: stream error or user canceled.\n", stderr);
return 0;
}
(note: instead of scanf for reading lines, fgets() is recommended, then simply trim the '\n' included in the filled buffer)
Example Use/Output
$ ./bin/oneline
enter z: is a fruit
Apple is a fruit
Use fgets() Instead
Instead of using scanf for line input, use a line-oriented input function like fgets() which will consume an entire line (including the line ending). The ensures your input buffer is left in a consistent state that does not depend on the previous format specifier user, e.g.
...
#include <string.h>
...
printf ("enter z: ");
if (fgets (z, sizeof z, stdin) != NULL) {
z[strcspn (z, "\n")] = 0; /* trim '\n' from end of z */
printf("%s %s\n",s,z);
}
Edit Per-Question in Comment
Your Problem With Your New Code Is scanf("%lf",&y); leaves the '\n' in stdin unread, you then attempt to read scanf("%[^105\n]",z); which reads nothing because you have excluded reading '\n' in the inverted character class and you then read stdin as input where the first character is '\n'. "%[^105\n]" means : read an unlimited number of characters and only stop the read if a 1, 0, 5 or '\n' character (or EOF) is encountered.
Taking mixed input with scanf is full of Pitfalls for new C programmers because of what is left in stdin, and how leading whitespace is handled depends on the format-specifier used. This is why fgets() (or POSIX getline()) are recommended for user input, and then parsing the needed information from the filled buffer with sscanf. With a line-oriented input function, the line is completely consumed on each input (given a sufficient buffer size -- don't skimp), eliminating the problems with scanf.
To make your current code work, you could do:
#include <stdio.h>
/* simple function to empty remainder of line in stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
int i = 4, x;
double d = 4.0, y;
char s[] = "Apple ", z[105];
scanf("%d",&x);
scanf("%lf",&y); /* leaves '\n' as next char in stdin */
empty_stdin(); /* empty extraneous characters */
scanf("%104[^\n]",z); /* read up to 104 chars, \n, or EOF */
printf("%d\n",i+x);
printf("%0.1lf\n",d+y);
printf("%s %s\n",s,z);
return 0;
}
(validate each call to scanf -- that is left to you)
Let me know if you have further questions.
I solved the problem by using \n in scanf("%lf\n",&y);
I have this program:
#include <stdio.h>
#define SIZE 19
int main(){
char string[SIZE];
while (string[0] != 'A'){
printf("\nEnter a new string.\n");
fgets(string,SIZE,stdin);
int storage = 0;
while (storage != '\n')
{
storage = getchar();
}
}
}
The nested while loop with getchar() exists in case the inputted string exceeds the maximum number of characters string[] can hold. If that is not there, inputting a string with, say, 20 characters, would cause the output to be:
Enter a new string.
12345123451234512345
Enter a new string.
Enter a new string.
The problem is that this requires me to press enter twice in order to enter a new string: once for 'fgets' and one for the nested while loop (this is my understanding of what's going on).
Is there a way to change this so I only have to press 'Enter' once, or possibly a way to change the entire while loop into something more elegant?
If the buffer that receives from fgets contains a newline, you know it read everything that was inputted so you don’t need to do anything else. If not, then you use the extra loop to flush the buffer.
You are thinking correctly, you just need to think through how and when you need to empty the input buffer a bit further.
All line-oriented input functions (fgets and POSIX getline) will read, and include, the trailing '\n' in the buffers they fill (fgets only when sufficient space is provided in the buffer).
When using fgets, you have only two possible returns, (1) a pointer to the buffer filled, or (2) "NULL on error or when end of file occurs while no characters have been read."
In case fgets returns a valid pointer, then it is up to you to determine whether a complete line of input was read, or whether the input exceeds the buffer size, leaving characters in the input buffer unread.
To make that determination, you check whether the last character in the buffer is '\n' and if not, whether the buffer contains SIZE-1 characters indicating that characters remain in the input buffer. You can do that a number of ways, you can use strchr (to get a pointer to the '\n'), strcspn (to get an index to it) or good old strlen (to get the length of the string) and then check the character at len-1.
(a note on preference, you can use whatever method you like, but in either case of strcspn or strlen, save the index or length so it can be used to validate whether the input exceeded the buffer size or whether the user ended input by generating a manual EOF. You save the index or length to prevent having to make duplicate function calls to either)
It is also helpful to create a simple helper-function to clear the input buffer to avoid placing loops everywhere you need the check. A simple function will do the trick, e.g.
/* simple function to empty stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
of if you prefer the more-compact, but arguably less readable version, a single for loop will do, e.g.
void empty_stdin (void)
{
for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}
}
The remainder of your example can be structured to complete each of the tests described above to provide input handling as you have described (although using the 1st character of the buffer being 'A' to control the loop is a bit strange), e.g.
#include <stdio.h>
#include <string.h>
#define STRSIZE 19
/* simple function to empty stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
char string[STRSIZE] = ""; /* initialize string all 0 */
while (*string != 'A') { /* up to you, but 'A' is a bit odd */
size_t len = 0; /* variable for strlen return */
printf ("enter a string: "); /* prompt */
if (!fgets (string, STRSIZE, stdin)) { /* validate read */
putchar ('\n'); /* tidy up with POSIX EOF on NULL */
break;
}
len = strlen (string); /* get length of string */
if (len && string[len-1] == '\n') /* test if last char is '\n' */
string[--len] = 0; /* overwrite with nul-character */
else if (len == STRSIZE - 1) /* test input too long */
empty_stdin(); /* empty input buffer */
}
return 0;
}
An arguably more useful approach is to have the loop exit if nothing is input (e.g. when Enter alone is pressed on an empty line). The test would then be while (*string != '\n'). A better approach rather is simply controlling your input loop with while (fgets (string, STRSIZE, stdin)). There, you have validated the read before entering the loop. You can also wrap the whole thing in a for (;;) loop and control the loop exit based on any input condition you choose.
Those are all possibilities to consider. Look things over and let me know if you have further questions.
fgets() does read the newline IF (and only if) the buffer is long enough to reach and contain it, along with a trailing nul terminator.
Your sample input is 20 characters, which will be followed by a newline, and then a nul terminator. That won't go into a buffer of 19 characters.
The simple way is to use fgets() in a loop, until the newline is included in the buffer.
printf("\nEnter a new string.\n");
do
{
fgets(string,SIZE,stdin);
/*
handle the input, noting it may or may not include a trailing '\n'
before the terminating nul
*/
} while (strlen(string) > 0 && string[strlen(string) - 1] != '\n');
This loop will clear input up to and including the first newline, and also allow you to explicit handle (discard if needed) ALL the input received. It is therefore not necessary to use a second loop with getchar().
You haven't checked if fgets() returns NULL, so neither have I. It is advisable to check, as that can indicate errors on input.
I have this assignment where I have to read till the "?" char and then check if it is followed by number and newline, or newline and then the number and than again newline.
I checked the first char after the "?"
if (scanf("%c",c)=='\n') ...;
but that only works if the first one is a newline, and when it isn't and i want to read the number instead, it cuts the first digit ... for example, it doesn´t read 133 but only 33
... how do i do this?
I also tried puting the char back, but that wouldn't work
please help :)
One advantage of getline over either fgets (or a distant scanf) is that getline returns the actual number of characters successfully read. This allows a simple check for a newline at the end by using the return to getline. For example:
while (printf ((nchr = getline (&line, &n, stdin)) != -1)
{
if (line[nchr - 1] = '\n') /* check whether the last character is newline */
line[--nchr] = 0; /* replace the newline with null-termination */
/* while decrementing nchr to new length */
Use fgets(3), or better yet, getline(3) (like here) to read the entire line, then parse the line using strtol(3) or sscanf(3) (like here)
Don't forget to carefully read the documentation of every function you are using. Handle the error cases - perhaps using perror then exit to show a meaningful message. Notice that scanf and sscanf return the number of scanned items, and know about %n, and that strtol can set some end pointer.
Remember that on some OSes (e.g. Linux), the terminal is a tty and is often line-buffered by the kernel; so nothing is sent to your program until you press the return key (you could do raw input on a terminal, but that is OS specific; consider also readline on Linux).
this line: if (scanf("%c",c)=='\n') ...; will NEVER work.
scanf returns a value that indicates the number of successful parameter conversions.
suggest:
// note: 'c' must be defined as int, not char
// for several reasons including:
// 1) getchar returns an int
// 2) on some OSs (dos/windows) '\n' is 2 characters long
// 3) if checking for EOF, EOF is defined as an int
if( '\n' == (c = getchar() ) )
{ // then found newline
...
#include <stdio.h>
int main (void){
int num;
scanf("%*[^?]?");//read till the "?"
while(1==scanf("%d", &num)){
printf("%d\n", num);
}
return 0;
}
DEMO
Relevant code snippet:
char input [1024];
printf("Enter text. Press enter on blank line to exit.\n");
scanf("%[^\n]", input);
That will read the whole line up until the user hits [enter], preventing the user from entering a second line (if they wish).
To exit, they hit [enter] and then [enter] again. So I tried all sorts of while loops, for loops, and if statements around the scanf() involving the new line escape sequence but nothing seems to work.
Any ideas?
Try this:
while (1 == scanf("%[^\n]%*c", input)) { /* process input */ }
As was yet pointed out, fgets() is better here than scanf().
You can read an entire line with fgets(input, 1024, stdin);
where stdin is the file associated to the standard input (keyboard).
The function fgets() reads every character from the keyboard up to the first new-line character: '\n' (obtained after pressing ENTER key, of course...).
Important: The character '\n' will be part of the array input.
Now, your next step is to verify if all the characters in the array input,
from the first to the '\n', are blanks.
Besides, note that all the characters after the first '\n' in input are garbage, so you have not to check them.
Your program could be as follows:
char input[1024];
printf("Enter text. Press enter on blank line to exit.\n");
while (1) {
if (fgets(input, 1024, stdin) == NULL)
printf("Input Error...\n");
else {
/* Here we suppose the fgets() has reached a '\n' character... */
for (char* s = input; (*s != '\n') && isspace(*s); s++)
; /* skipping blanks */
if (*s == '\n')
break; /* Blank line */
else
printf("%s\n", input); /* The input was not a blank line */
}
}
That code must be written inside your main() block and,
more importantly, it is necessary to include the header <ctype.h> before all,
because the isspace() function is used.
The code is simple: the while is executed for ever, the user enter a line in each iteration, the if sentences checks if some error has happened.
If everything was fine, then a for(;;) statement is executed, which explores the array input to watch if there are just blanks there... or not.
The for iterations continue up to the first new-line '\n' is found, or well, a non-blank character appears.
When for terminates, it means that the last analyzed character, which is held in *s, is a newline (meaning that all earlier characters were blanks), or not (meaning that at least there is some non-blank character in input[], so input is a normal text).
The "ethernal" while(1) is broken only in case that a blank-line is
read (see the break statement in 11th line).
OP says "To exit, they hit [enter] and then [enter] again"
unsigned ConsecutiveEnterCount = 0;
for (;;) {
char buffer[1024];
if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
break; // handle error or EOF
}
if (buffer[0] == '\n') {
ConsecutiveEnterCount++;
if (ConsecutiveEnterCount >= 2 /* or 1, not clear on OP intent */) {
break;
}
}
else ConsecutiveEnterCount = 0;
// Do stuff with buffer;
}
#include <stdio.h>
int main(){
char arr[40];
int i;
for( i = 0; i < sizeof(arr); i +=2 ){
scanf("%c%c",&arr[i],&arr[i+1]);
if( arr[i] == '\n' && arr[i+1] == '\n' )
break;
}
printf("%s", arr);
return 0;
}
... I tried all sorts of while loops, for loops, and if statements around the scanf() involving the new line escape sequence but nothing seems to work.
It seems you tried everything that you shouldn't have tried, prior to reading! A C programmer is expected to read manuals lest they want to run into undefined behaviour which causes headaches like the one you've experienced. To elaborate, you can't learn C by guessing like you can Java.
Consider this your lesson. Stop guessing and start reading (the fscanf manual)!
According to that manual:
[ Matches a non-empty sequence of bytes from a set of expected bytes (the scanset).
The emphasis is mine. What you seem to be describing is an empty sequence of bytes, which means that the match fails. What does the manual say about matching failures?
Upon successful completion, these functions shall return the number of successfully matched and assigned input items; this number can be zero in the event of an early matching failure. If the input ends before the first conversion (if any) has completed, and without a matching failure having occurred, EOF shall be returned. If an error occurs before the first conversion (if any) has completed, and without a matching failure having occurred, EOF shall be returned...
Again, the emphasis is mine... This is telling you that like most other C-standard functions, you need to check the return value! For example, when you call fopen you then write some idiom along the lines of if (fp == NULL) { /* handle error */ }.
Where's your error handling? Note that the return value isn't merely a binary selection; where n conversions are performed, there are n+2 possible return values in the range of: EOF, 0 .. n. You should understand what each of those means, before you try to use fscanf.
Code:
#include <stdio.h>
int main(void) {
char i[50];
while(scanf("%s ", i)){
printf("You've written: %s \n", i);
}
printf("you have finished writing\n");
return 0;
}
One problem is that the code doesn't do as it is expected to. If I typed in:
abc def ghi.
It would output:
You've written: abc
You've written: def
How can I fix it? The goal is to read every single word from stdin until it reaches "ENTER" or a "." (dot).
#cnicutar is pretty close, but you apparently only want to start reading at something other than white-space, and want to stop reading a single word when you get to whitespace, so for you scanset, you probably want something more like:
while(scanf(" %49[^ \t.\n]%*c", i)) {
In this, the initial space skips across any leading white space. The scan-set then reads until it gets to a space, tab, new-line or period. The %*c then reads (but throws away) the next character (normally the one that stopped the scan).
This can, however, throw away a character when/if you reach the end of the buffer, so you may want to use %c, and supply a character to read into instead. That will let you recover from a single word longer than the buffer you supplied.
How about:
scanf("%49[ ^\n.]", str)
Or something like that.
Ditch scanf altogether and go with fgets:
while (fgets(i, sizeof i, stdin))
{
printf("you've written: %s\n", i);
}
with the following caveats:
If there's room in the target buffer, fgets will store the trailing newline as part of the input;
If you want to stop reading on finding a ., you'll have to add some logic to look for it in the input string, such as the following:
int foundDot = 0;
while (fgets(i, sizeof i, stdin) && !foundDot)
{
char *dot = strchr(i, '.');
char *newline = strchr(i, '\n');
if (dot != NULL)
{
foundDot = 1;
*dot = 0; // overwrite the '.' character with the 0 terminator
}
if (newline != NULL)
{
*newline = 0; // overwrite newline character with 0 terminator
}
/**
* Assuming you don't want to print a blank line if you find a dot
* all by itself.
*/
if (strlen(i) > 0)
printf("you've written: %s\n", i);
}
The easiest way to do this is with flex. Otherwise you are repeating a bunch of difficult, complex work, and are likely to make mistakes.
Also, read lex and yacc, 2nd edition.