C - Ignore spaces in scanf() - c

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

Related

Usage of scanf ... getchar

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

How to print two strings on the same line in c

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);

Counting the Whole String Including The Spaces in C

I'm trying to set up a code that counts the whole string and doesn't stop after the first space that it finds. How do I do that?
I tried this kind of code but it just counts the first word then goes to display the number of letters in that first word.
So far this is what I have tried.
int main(){
char get[100];
int i, space=0, len=0, tot;
scanf("%s", get);
for (i=0; get[i]!='\0'; i++)
{
if (get[i] == ' ')
space++;
else
len++;
}
tot = space + len;
printf("%i", tot);
}
And
int main(){
char get[100];
int len;
scanf("%s", &get);
len = strlen(get);
printf("%i", len);
}
But would still get the same answer as the first one.
I expected that if the
input: The fox is gorgeous.
output: 19
But all I get is
input: The fox is gorgeous.
output: 3
strlen already includes spaces, since it counts the length of the string up to the terminating NUL character (zero, '\0').
Your problem is that that the %s conversion of scanf stops reading when it encounters whitespace, so your string never included it in the first place (you can verify this easily by printing out the string). (You could fix it by using different scanf conversions, but in general it's easier to get things right by reading with fgets – it also forces you to specify the buffer size, fixing the potential buffer overflow in your current code.)
The Answer by Arkku is correct in its diagnose.
However, if you wish to use scanf, you could do this:
scanf("%99[^\n]", get);
The 99 tells scanf not to read more than 99 characters, so your get buffer won't overflow. The [^\n] tells scanf to read any character until it encounters the newline character (when you hit enter).
As Chux pointed out, the code still has 2 issues.
When using scanf, it is always a good idea to check its return value, which is the number of items it could read. Also, indeed the \n remains in the input buffer when using the above syntax. So, you could do this:
if(scanf("%99[^\n]", get) == 0){
get[0] = 0; //Put in a NUL terminator if scanf read nothing
}
getchar(); //Remove the newline character from the input buffer
I will take one example to explain the concept.
main()
{
char s[20], i;
scanf("%[^\n]", &s);
while(s[i] != '\0') {
i++;
}
printf("%d", i);
return 0;
}
i have used c language and u can loop through the ending the part of the string and you will get the length. here i have used "EDIT SET CONVESRION METHOD" to read string, you can also gets to read.

Search whitespace in string inC

problem is when I try to enter a string with space compiler render that as separate 2 strings. But requirement is whenever there is a space in string don't treat it as 2 strings,but rather a single string. The program should print yes only if my four inputs are MAHIRL,CHITRA,DEVI and C. my code is:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[10],str2[10],str3[10],str4[10];
scanf("%s",str1);
scanf("%s",str2);
scanf("%s",str3);
scanf("%s",str4);
if(strcmp(str1,"MAHIRL")==0 && strcmp(str2,"CHITRA")==0 && strcmp(str3,"DEVI")==0 && strcmp(str4,"C")==0 ){
printf("yes");
}
else{
printf("no");
}
return 0;
}
I tried using strtok() and strpbrk(), but I'm not quite sure how to implement them in my code. Any help or suggestion is appreciated. Thanks.
problem is when I try to enter a string with space compiler render that as separate 2 strings
That's not a problem, that's the feature / behaviour of %s format specifier with scanf(). You cannot read space-delimited input using that.
For conversion specifier s, chapter §7.21.6.2, C11
s Matches a sequence of non-white-space characters. [...]
So, the matching ends as soon as it hits a white-space character, here, the space.
If you have to read a line (i.e., input terminated by newline), use fgets() instead.
The %s directive matches characters up to a whitespace before storing them, so it is not possible to get lines of input this way. There are other ways to use scanf() to read lines of input, but these are error-prone, and this is really not the right tool for the job.
Better to use fgets() to fetch a line of input to a buffer, and sscanf() to parse the buffer. Since the requirement here is that four strings are entered, this is a simple problem using this method:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[10],str2[10],str3[10],str4[10];
char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
return 1;
}
if (sscanf(buffer, "%9s%9s%9s%9s", str1, str2, str3, str4) == 4) {
if (strcmp(str1,"MAHIRL") == 0 &&
strcmp(str2,"CHITRA") == 0 &&
strcmp(str3,"DEVI") == 0 &&
strcmp(str4,"C") == 0 ){
printf("yes\n");
} else {
printf("no\n");
}
} else {
printf("Input requires 4 strings\n");
}
return 0;
}
An additional character array is declared, buffer[], with enough extra space to contain extra input; this way, if the user enters some extra characters, it is less likely to interfere with the subsequent behavior of the program. Note that fgets() returns a null pointer if there is an error, so this is checked for; an error message is printed and the program exits if an error is encountered here.
Then sscanf() is used to parse buffer[]. Note here that maximum widths are specified with the %s directives to avoid buffer overflow. The fgets() function stores the newline in buffer[] (if there is room), but using sscanf() in this way avoids needing to further handle this newline character.
Also note that sscanf() returns the number of successful assignments made; if this return value is not 4, the input was not as expected and the values held by str1,..., str4 should not be used.
Update
Looking at this question again, I am not sure that I have actually answered it. At first I thought that you wanted to use scanf() to read a line of input, and extract the strings from this. But you say: "whenever there is a space in string don't treat it as 2 strings", even though none of the test input in your example code contains such spaces.
One option for reading user input containing spaces into a string would be to use a separate call to fgets() for each string. If you store the results directly in str1,...,str4 you will need to remove the newline character kept by fgets(). What may be a better approach would be to store the results in buffer again, and then to use sscanf() to extract the string, this time including spaces. This can be done using the scanset directive:
fgets(buffer, sizeof buffer, stdin);
sscanf(buffer, " %9[^\n]", str1);
The format string here contains a leading space, telling sscanf() to skip over zero or more leading whitespace characters. The %[^\n] directive tells sscanf() to match characters, including spaces, until a newline is encountered, storing them in str1[]. Note that a maximum width of 9 is specified, leaving room for the \0 terminator.
If you want to be able to enter multiple strings, each containing spaces, on the same line of user input, you will need to choose a delimiter. Choosing a comma, this can be accomplished with:
fgets(buffer, sizeof buffer, stdin);
sscanf(buffer, " %9[^,], %9[^,], %9[^,], %9[^,\n]", str1, str2, str3, str4);
Here, there is a leading space as before, to skip over any stray whitespace characters (such as \n characters) that may be in the input. The %[^,] directives tell sscanf() to match characters until a comma is encountered, storing them in the appropriate array (str1[],..., str3[]). The following , tells sscanf() to match one comma and zero or more whitespace characters before the next scanset directive. The final directive is %[^,\n], telling sscanf() to match characters until either a comma or a newline are encountered.
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[10],str2[10],str3[10],str4[10];
char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
return 1;
}
/* Each individual string str1,..., str4 may contain spaces */
if (sscanf(buffer, " %9[^,], %9[^,], %9[^,], %9[^,\n]",
str1, str2, str3, str4) == 4) {
if (strcmp(str1,"test 1") == 0 &&
strcmp(str2,"test 2") == 0 &&
strcmp(str3,"test 3") == 0 &&
strcmp(str4,"test 4") == 0 ){
printf("yes\n");
} else {
printf("no\n");
}
} else {
printf("Input requires 4 comma-separated strings\n");
}
return 0;
}
Here is a sample interaction with this final program:
test 1, test 2, test 3, test 4
yes
While reading strings from user, use __fpurge(stdin) function from stdio_ext.h. This flushes out the stdin. When you are entering a string, you press enter at last, which is also a character. In order to avoid that, we use fpurge function.
scanf("%s",str1);
__fpurge(stdin);
scanf("%s",str2);
__fpurge(stdin);
scanf("%s",str3);
__fpurge(stdin);
scanf("%s",str4);
__fpurge(stdin);
Also if you want to input a string from user containing spaces, use following:
scanf("%[^\n]", str1);
This will not ignore the spaces you enter while inputting string.
EDIT: Instead of using fpurge function, one can use following code:
while( getchar() != '\n' );

ANSI C - how to read from stdin word by word?

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.

Resources