问题
See this main
:
int main(void)
{
int i;
int ch;
char str[512];
fgets(str, sizeof str, stdin);
for (i = 0; i <= (strlen(str)); i++)
{
if (str[i] != '\0' && str[i] != '\n')
{
int num = atoi(&str[i]);
printf("%d\n", num);
}
}
return 0;
}
I want to get line with numbers from user and get all the numbers without any spaces
or tabs
.
For example:
The input 1 2 3
.
But in this case this the output:
1
2
2
3
3
So why i received 2
and 3
twice?
回答1:
Here's how I would do it:
char line[256];
if (fgets(line, sizeof line, stdin) != NULL)
{
const char *ptr = line;
while (*ptr != '\0')
{
char *eptr = NULL;
const long value = strtol(ptr, &eptr, 10);
if (eptr != ptr)
printf("%ld\n", value);
else
break;
ptr = eptr;
}
}
This uses strtol() so it will also handle negative numbers; if this is incorrect you can of course add checks to filter them out. I think this is way better than anything using strtok()
.
回答2:
Because the you also pass the position of the string which starts with spaces. they get the first number to be 2
and 3
respectively twice. That is what is returned.
for (i = 0; i <= (strlen(str)); i++)
{
if (str[i] != '\0' && !isspace(str[i]) )
{
int num = atoi(&str[i]);
printf("%d\n", num);
}
}
Prints:
1
2
3
For the purpose of tokenizing you can use strtok
and to convert it to number strtol
etc. These provides much better control over the error cases than atol/atoi
do.
回答3:
When it reaches the space character in the input it will call atoi()
with " 2 3"
(resulting in 2) and later on " 3"
(resulting in 3) which creates the unexpected numbers.
回答4:
From the ref of atoi()
:
The function first discards as many whitespace characters (as in isspace) as necessary until the first non-whitespace character is found. [...]
That means that if you give as input to that function " 2", it will return 2.
Change this:
if (str[i] != '\0' && str[i] != '\n')
to this:
if (str[i] != ' ' && str[i] != '\0' && str[i] != '\n')
and you will get:
1
2
3
Here is a tip about debugging this code: In your output, you got 2 and 3 twice, but not 1.
In other words, you get the number that were after a space twice. 1 didn't have a space before it.
This should make you thing that there is something spooky about the spaces there.
Indeed, you would enter the body of the if statement, even if str[i]
was a space!
By adding a condition to check if the current character is not a space, to enter the boby of the if statement, you actually skip the spaces.
回答5:
It is a good idea to always think of error handling. What if the user wrongly press space and tabs. So first remove spaces and tabs if exist :
char *c = string;
while ((*c == ' ') || (*c == '\t'))
++c;
and then use atoi()
.
回答6:
The solution with strtok
isn't that hard either:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char s[128];
fgets(s, sizeof s, stdin);
const char *delim = " \t\n";
char *p = strtok(s, delim);
while(p) {
int val = strtol(p, NULL, 10);
printf("%d\n", val);
p = strtok(NULL, delim);
}
return 0;
}
Though do keep in mind that it's a bit iffy in that it uses hidden state (not good for multithreaded programs), and of course it modifies the input string, so it can't be a constant (but why would you do that).
回答7:
You can use isdigit
or isalpha()
function (based upon your use) available in ctype.h
. following is code snippet using isdigit
function:
for (i = 0; i <= (strlen(str)); i++)
{
if (isdigit(str[i]))
{
int num = atoi(&str[i]);
if(i && str[i-1]=='-') num *= -1;
printf("%d\n", num);
i += ( num==0 ) ? 1 : (int)log10(abs(num))+1;
}
}
See it working here.
Check here for example on isdigit
and isalpha()
functions.
Regarding your question that:
So why i received 2 and 3 twice?
See following explanation available at cplusplus.com which explains the atoi()
function.
The function first discards as many whitespace characters (as in isspace) as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many base-10 digits as possible, and interprets them as a numerical value.
来源:https://stackoverflow.com/questions/47451405/c-programming-print-only-int-from-fgets