Is the following pattern ok in C to get a string up until a newline?
int n = scanf("%40[^\n]s", title);
getchar();
It seems to work in being a quick way to strip off the trailing newline, but I'm wondering if there are shortcomings I'm not seeing here.
The posted code has multiple problems:
the s in the format string is not what you think it is: the specification is %40[^\n] and the s will try and match an s in the input stream, which may occur after 40 bytes have been stored into title.
scanf() will fail to convert anything of the pending input is a newline, leaving title unchanged and potentially uninitialized
getchar() will not necessarily read the newline: if more than 40 characters are present on the line, it will just read the next character.
If you want to read a line, up to 40 bytes and ignore the rest of the line up to and including the newline, use this:
char title[41];
*title = '\0';
if (scanf("%40[^\n]", title) == EOF) {
// end of file reached before reading anything, handle this case
} else {
scanf("%*[^\n]"); // discard the rest of the line, if any
getchar(); // discard the newline if any (or use scanf("%1*[\n]"))
}
It might be more readable to write:
char title[41];
int c, len = 0;
while ((c = getchar()) != EOF && c != '\n') {
if (len < 40)
title[len++] = c;
}
title[len] = '\0';
if (c == EOF && len == 0) {
// end of file reached before reading a line
} else {
// possibly empty line of length len was read in title
}
You can also use fgets():
char title[41];
if (fgets(title, sizeof title, stdin) {
char *p = strchr(title, '\n');
if (p != NULL) {
// strip the newline
*p = '\0';
} else {
// no newline found: discard reamining characters and the newline if any
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
}
} else {
// at end of file: nothing was read in the title array
}
Previous note, the s should be removed, it's not part of the specifier and is enough to mess up your read, scanf will try to match an s character against the string you input past the 40 characters, until it finds one the execution will not advance.
To answer your question using a single getchar is not the best approach, you can use this common routine to clear the buffer:
int n = scanf(" %40[^\n]", title);
int c;
while((c = getchar()) != '\n' && c != EOF){}
if(c == EOF){
// in the rare cases this can happen, it may be unrecoverable
// it's best to just abort
return EXIT_FAILURE;
}
//...
Why is this useful? It reads and discards all the characters remaing in the stdin buffer, regardless of what they are.
In a situation when an inputed string has, let's say 45 characters, this approach will clear the stdin buffer whereas a single getchar only clears 1 character.
Note that I added a space before the specifier, this is useful because it discards all white spaces before the first parseable character is found, newlines, spaces, tabs, etc. This is usually the desired behavior, for instance, if you hit Enter, or space Enter it will discard those and keep waiting for the input, but if you want to parse empty lines you should remove it, or alternatively use fgets.
There are a number of problems with your code like n never being used and wrong specifier for scanf.
The better approach is to use fgets. fgets will also read the newline character (if present before the buffer is full) but it's easy to remove.
See Removing trailing newline character from fgets() input
Related
I'm trying to make a simple string acquisition. What i need is to write a string from input (stdin), which can contain spaces, and save it without any spaces between words.
So far i've written this simple code which saves everything (also spaces), but i don't know how to make the scanf() ignore the spaces.
int main(){
char str[10];
scanf("%[^\n]s, str);
printf("%s", str;
}
For example:
if my input is: I love C programming! my output should be: IloveCprogramming!
I tried to use %*, used to ignore characters, but without any success.
I also know that i could "rescan" the string once is saved and remove all the spaces, but i need to do this acquisition as efficient as possible, and rescan every string to remove the spaces will increase the computational time a lot (instead of just scanning and ignoring, which has complexity of O(n))
You are using the wrong tool for the job. You need to use getc
And do the following
int ch;
char str[10];
// Loop until either loop reaches 9 (need one for null character) or EOF is reached
for (int loop = 0; loop < 9 && (ch = getc(stdin)) != EOF; ) {
if (ch != ' ' ) {
str[loop] = ch;
++loop;
}
}
str[loop] = 0;
printf("%s", str);
No re-scan required
scanf() is not useful for your purpose, indeed you do not even need a buffer to strip spaces from a line of input: just read bytes one at a time, ignore the spaces, output the others and stop at newline or EOF:
#include <stdio.h>
int main(void) {
int c;
while ((c = getchar()) != EOF) {
if (c != ' ') {
putchar(c);
}
if (c == '\n') {
break;
}
}
return 0;
}
Note also that your code has problems:
the scanf() format string is unterminated
the trailing s is incorrect, the format is simply %[^\n]
it is safer to specify the maximum number of bytes to store into the array before the null terminator: scanf("%9[^\n]", str);
you should test the return value of scanf() to avoid passing an uninitialize array to printf if the conversion fails, for example on an empty line or an empty file.
You could use scanf() as an inefficient way too read characters while ignoring white space, with char c; while (scanf(" %c", &c) == 1) { putchar(c); } but you would be unable to detect the end of line.
If interested in removing other white space from input (in addition to '') you can also incorporate the C library function isspace(.), which tests for the following standard white space characters:
' ' (0x20) space (SPC)
'\t' (0x09) horizontal tab (TAB)
'\n' (0x0a) newline (LF)
'\v' (0x0b) vertical tab (VT)
'\f' (0x0c) feed (FF)
'\r' (0x0d) carriage return (CR)
This example incorporates function using the isspace(.); library function, and provides a method to clear all standard white space from a C string.
int main(void)
{
char string[] = {"this contain's \n whitespace\t"};
int len = strlen(string);
char out[len+1];// +1 for null terminator
//(accommodates when input contains no whitespace)
int count = clean_whitespace(string, out);
return 0;
}
int clean_whitespace(const char *in, char *out)
{
int len, count=0, i;
if((in) && (out))
{
len = strlen(in);
for(i=0;i<len;i++)
{
if(!isspace(in[i]))
{
out[count++] = in[i];
}
}
out[count]=0;//add null terminator.
}
return count;
}
So far i've written this simple code which saves everything (also
spaces), but i don't know how to make the scanf() ignore the spaces.
You're coming at this from the opposite direction of what most new C programmers do. The problem is not usually to make scanf skip spaces, as it does that by default for most types of field, and in particular for %s fields. Spaces are ordinarily recognized as field delimiters, so not only are leading spaces skipped, but also spaces are not read inside fields. I presume that it is because you know this that you are using a %[ field.
But you cannot have your cake and eat it too. The field directive %[^\n] says that the data to be read consist of a run of non-newline characters. scanf will faithfully read all such characters and transfer them to the array you designate. You do not have the option to instruct scanf to avoid transferring some of the characters that you told it were part of the field.
If you want to continue to use scanf then you have two options:
remove the spaces after you read the data, or
read and transfer the space-separated pieces as separate fields.
Another answer already describes how to do the former. Here's how you might do the latter:
int main(void) {
int field_count;
do {
char str[80];
char tail;
field_count = scanf("%79[^ \n]%c", str, &tail));
if (field_count == 0) {
// No string was scanned this iteration: the first available char
// was a space or newline. Consume it, then proceed appropriately.
field_count = scanf("%c", &tail);
if (field_count != 1 || tail == '\n') {
// newline, end-of-file, or error: break out of the loop
break;
} // else it's a space -- ignore it
} else if (field_count > 0) {
// A string was scanned; print it:
printf("%s", str);
if (field_count == 2) {
// A trailing character was scanned, too; take appropriate action:
if (tail == '\n') {
break;
} else if (tail != ' ') {
putchar(tail);
} // else it is a space; ignore it
}
} // else field_count == EOF
} while (field_count != EOF);
}
Things to note:
The 79-character (maximum) field width in the scanf %79[^ \n] directive. Without a field width, there is a serious risk of overrunning your array bound (which must be at least one character longer than the field to allow for a string terminator).
[ is a field type, not a qualifier. s is a separate field type that also handles strings, but has different behavior; no s field is used here.
scanf's return value tells you how many fields were successfully scanned, which can be fewer than are described in the format string in the event that a mismatch occurs between input and format, or the end of the input is reached, or an I/O error occurs. These possibilities need to be taken into account.
In the event that the second field, %c, is in fact scanned, it allows you to determine whether the preceding string field ended because the field width was exhausted without reaching a space or newline, because a space was observed, or because a newline was observed. Each of these cases requires different handling.
Although scanf skips leading whitespace for most field types, %[ and %c fields are two of the three exceptions.
This approach skips space characters (' ') specifically; it does not skip other whitespace characters such as horizontal and vertical tabs, carriage returns, form feeds, etc.. This approach could be adapted to handle those, too, but what is presented is sufficient to demonstrate.
I'm posting this to demonstrate that it is also possible to solve this problem just with scanf.
int main() {
char a[10];
for(int i = 0; i < 10 ; i++){
scanf("%c", &a[i]);
if( a[i] == ' ')
i--;
}
}
the one above simply scans 10 characters without the spaces inbetween.
for(int i = 0; i < 9; i++){
printf("%c,", a[i]);
}
printf("%c", a[9]);
and this is the way to use if you want to replace the spaces with something else, for example: ' , '
If you want the input to consist of more characters, simply define a new variable x and change the 10 into x, and the 9's into x-1
For completeness, here's a simple version using scanf():
#include <stdio.h>
int main(void)
{
char buff[10];
int r;
r = 1;
scanf("%*[ ]");
while (r == 1) {
r = scanf("%9[^ \n]%*[ ]", buff);
if (r == 1) fputs(buff, stdout);
}
putchar('\n');
return 0;
}
What this does:
scanf("%*[ ]"): skip initial whitespace, if any (requested format: non empty sequence of spaces, without storing it in a variable), ignoring the result.
r = scanf("%9[^ \n]%*[ ]", buff): read two requested formats (explained below) and return number of successful conversions.
%9[^ \n]: requested format: up to 9 characters of contiguous text (read up to newline).
%*[ ]: requested format: non empty sequence of spaces, without storing it in a variable.
if (r == 1) fputs(buff, stdout): check if some text was read (1 successful conversion from scanf()). If it was, output it.
This is executed in a loop until a text slice cannot be read anymore. Optionally, the final \n can be read with getchar().
Example execution:
$ ./scanstring
abcd xyz aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wwwwwwwwwwwwwwwwwwwwwwwwwwww zzzzz 1234567891011121314 !!!
abcdxyzaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwwwzzzzz1234567891011121314!!!
scanf() manual: https://man7.org/linux/man-pages/man3/scanf.3.html
I'm trying to read a line from stdin but I don't know to properly handle the cases when input size is at least equal to the limit. Example code:
void myfun() {
char buf[5];
char somethingElse;
printf("\nInsert string (max 4 characters): ");
fgets (buf, 5, stdin);
...
printf("\nInsert char: ");
somethingElse = getchar();
}
Now, the user can do three things:
Input less than 4 characters (plus newline): in this case there's nothing left in stdin and the subsequent getchar() correctly waits for user input;
Input exactly 4 characters (plus newline): in this case there's a newline left in stdin and the subsequent getchar() reads it;
Input more than 4 characters (plus newline): in this case there's at least another character left in stdin and the subsequent getchar() reads it, leaving at least a newline in.
Cases 2 and 3 would require emptying stdin using something like while(getchar() != '\n'), whereas case 1 doesn't require any additional action. As I understand from reading answers to similar questions and c-faq, there's no standard/portable way to know whether the actual scenario is the one described in 1 or not.
Did I get it well? Or there actually is a portable way to do it? Or maybe a totally different approach?
The fgets function will store the newline in the buffer if there is room for it. So if the last character in the string is not a newline, you know you need to flush the buffer.
fgets (buf, 5, stdin);
if (strrchr(buf, '\n') == NULL) {
// flush buffer
int c;
while ((c = getchar()) != '\n') && (c != EOF));
}
If ones assumes that a null character '\0' is never read, then #dbush answer will work.
If a null character is read, then strrchr(buf, '\n') does not find any '\n' that may have been read.
Code could pre-set the buffer to see if a '\n' was read in the end.
buf[sizeof buf - 2] = '\n';
if (fgets (buf, sizeof buf, stdin)) {
if (strrchr(buf, '\n') == NULL) {
// incomplete line read. (the usual detection)
} else if (buf[sizeof buf - 2] != '\n') {
// incomplete line read with prior null character (see below note).
}
}
Yet the C standard does not specify that data past what was read in buf[] is unchanged, pre-filling the buffer with some pattern is not sufficient to detect if a null character '\0' was read.
is a portable way to do it?
The most portable way is to use repeated calls to fgetc() or the like instead of fgets().
maybe a totally different approach?
I recommend fgetc() or the common but not C standard getline()
Another alternative: Use scanf("%4[^\n]%n", buf, &n): It is very cumbersome, yet a portable way is possible. It keeps track of the number of characters read before the '\n' even if some are null characters.
int n = 0;
cnt = scanf("%4[^\n]%n", buf, &n); // Cumbersome to get that 4 here.
// Lots of TBD code now needed:
// Handle if cnt != 1 (\n to be read or EOF condition)
// Handle if n == sizeof buf - 1, (0 or more to read)
// Handle if n < sizeof buf - 1, (done, now get \n)
// A \n may still need to be consumed
// Handle EOF conditions
Is it possible to read an entire string including blank spaces like gets() function in scanf()?
I am able to do it using the gets() function.
char s[30];
gets(s);
This will read a line of characters. Can this be done in scanf()?
You can read a line, including blank spaces, with scanf(), but this function is subtle, and using it is very error-prone. Using the %[^\n] conversion specifier, you can tell scanf() to match characters to form a string, excluding '\n' characters. If you do this, you should specify a maximum field width. This width specifies the maximum number of characters to match, so you must leave room for the '\0' terminator.
It is possible that the first character in the input stream is a '\n'. In this case, scanf() would return a value of 0, since there were no matches before encountering the newline. But, nothing would be stored in s, so you may have undefined behavior. To avoid this, you can call scanf() first using the %*[\n] conversion specifier, discarding any leading '\n' characters.
After the string has been read, there will be additional characters in the input stream. At least a '\n' is present, and possibly more characters if the user entered more than the maximum field width specifies. You might then want to discard these extra characters so that they don't interfere with further inputs. The code below includes a loop to do this operation.
The first call to scanf() will consume all newline characters in the input stream until a non-newline character is encountered. While I believe that the second call to scanf() should always be successful, it is good practice to always check the return value of scanf() (which is the number of successful assignments made). I have stored this value in result, and check it before printing the string. If scanf() returns an unexpected result, an error message is printed.
It is better, and easier, to use fgets() to read entire lines. You must remember that fgets() keeps the trailing newline, so you may want to remove it. There is also a possibility that the user will enter more characters than the buffer will store, leaving the remaining characters in the input stream. You may want to remove these extra characters before prompting for more input.
Again, you should check the return value of fgets(); this function returns a pointer to the first element of the storage buffer, or a NULL pointer in the event of an error. The code below replaces any trailing newline character in the string, discards extra characters from the input stream, and prints the string only if the call to fgets() was successful. Otherwise, an error message is printed.
#include <stdio.h>
int main(void)
{
char s[30];
int result;
printf("Please enter a line of input:\n");
scanf("%*[\n]"); // throw away leading '\n' if present
result = scanf("%29[^\n]", s); // match up to 29 characters, excluding '\n'
/* Clear extra characters from input stream */
int c;
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
if (result == 1) {
puts(s);
} else {
fprintf(stderr, "EOF reached or error in scanf()\n");
}
printf("Please enter a line of input:\n");
char *ps = fgets(s, 30, stdin); // keeps '\n' character
if (ps) {
while (*ps && *ps != '\n') {
++ps;
}
if (*ps) { // replace '\n' with '\0'
*ps = '\0';
} else {
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
}
puts(s);
} else {
fprintf(stderr, "EOF reached or error in fgets()\n");
}
return 0;
}
Note that these two methods of getting a line of input are not exactly equivalent. The scanf() method, as written here, does not accept an empty line (i.e., a line consisting of only the '\n' character), but does accept lines consisting of other whitespace characters. The fscanf() method will accept an empty line as input.
Also, if it is acceptable to ignore leading whitespace characters, it would be simpler to follow the recommendation given by Jonathan Leffler in the comments to use only a single call to scanf():
result = scanf(" %29[^\n]", s);
This will ignore leading whitespace characters, including newlines.
Do not use scanf() or gets() function — use fgets() instead. But for the above question please find the answer.
int main() {
char a[30];
scanf ("%29[^\n]%*c", name);
printf("%s\n", a);
return 0;
}
Its also highly recommended like I told in the beginning to use fgets() instead. We clearly do not understand the weird requirement. I would have used the fgets() to read the character.
fgets(a, size(a), stdin);
I have the following program written in C:
The main problem with this program is that if the input exceeds 80 characters when using fgets() function, the program just exits immediately. The other code is executed, however it does not wait for the user to press enter. It like simply ignores the getchar at the end.
How can I solve this problem please?
If the user input is longer than the 79 characters that fgets may read from stdin (it can read at most one less than its size parameter says, since it 0-terminates the buffer), the remaining input is left in the input buffer, hence the getchar() at the end immediately succeeds.
To avoid that, you need to clear the input buffer if the input was too long.
The problem is that if the input was short enough, you don't know whether to clear the buffer or not. So check whether you actually got a newline read in by fgets,
int len = strlen(password);
if (password[len-1] == '\n') {
// got a newline, all input read, overwrite newline
password[len-1] = 0;
} else {
// no newline, input too long, clear buffer
int ch;
while ((ch = getchar()) != EOF && ch != '\n');
if (ch == EOF) {
// input error, stdin closed or corrupted, what now?
}
}
Check if a new-line character was read by fgets(), and if not skip input until a new-line character is encountered:
if (0 == strrchr(password, '\n'))
{
/* Skip until new-line. */
int c;
while (EOF != (c = getchar()) && '\n' != c);
}
otherwise the call to getchar() will read what fgets() did not.
will this actually verify my users input has only two elements then a newline?
char newline
scanf(" %s %s%c", out, in, &newline);
if(newline != '\n'){
error();
}
You must check the return status from scanf(); if it does not return 3, you failed the validation. Your checks will ensure that there are two 'words' (possibly preceded by some space). You won't be allowed trailing space after the second word. Note that you might need to 'eat' the rest of the line if the validation fails - you won't if you have a newline in newline.
Yes, that will work. But only if the input matches exactly: word word<enter>.
If the user types anything different from that format, for instance, a space between the 2nd word and enter, your logic will fail.
char newline;
char out[50];
char in[50];
scanf("%s %s%c", out, in, &newline);
if(newline != '\n')
{
printf("error!");
}
Also, scanf shouldn't be used to read from the input like that. Consider using fgets to read the input and strtok to parse the data.
No! scanf will work incorrect if there are some whitespace character between second string and newline. use getc() for that:
scanf("%s%s", buf1, buf2);
char newline = 0;
while((newline = getc()) == ' ' || newline == '\t');
if(newline != '\n'){
error();
}
Edit:
Adding case for trailing whitespaces.