8

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?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
user2908206
  • 265
  • 3
  • 16
  • 2
    How do you develop your code? Do you just edit it in Notepad? Get yourself an IDE, such as Eclipse CDT, NetBeans, MS Visual Studio, etc. Learn how to set breakpoints, step through the code, line by line, examine variables. You have just been taught [how to fish](https://en.wiktionary.org/wiki/give_a_man_a_fish_and_you_feed_him_for_a_day;_teach_a_man_to_fish_and_you_feed_him_for_a_lifetime) :-) – Mawg says reinstate Monica Nov 23 '17 at 09:08
  • 2
    You have a `for` loop that, for no logical reason whatsoever, attempts to process every character in the input and output some number for it. It's immediately obvious that for "1 2 3", five numbers will be printed, since there are five characters in that string that are not nul or newline. – David Schwartz Nov 23 '17 at 10:13
  • 2
    @Mawg to fish you don't need a trawler: a fishing rod would be enough in many cases (a simple debugger instead of an IDE, in this analogy). – Ruslan Nov 23 '17 at 11:00
  • A good point, but a visual debugger is simpler. Rather the IDE showing GDB, thak just running GDB from the command line – Mawg says reinstate Monica Nov 23 '17 at 11:51
  • 1
    You want to get the numbers without spaces or tabs, but nowhere do you check for either spaces or tabs. – Bernhard Barker Nov 23 '17 at 14:30

7 Answers7

6

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().

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    Perhaps mention that, in addition to not telling you where it stopped parsing, `atoi()` also triggers UB in case of overflow... `strto*()` is definitely the right choice here. – DevSolar Nov 23 '17 at 13:55
5

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.

user2736738
  • 30,591
  • 5
  • 42
  • 56
1

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.

neuhaus
  • 3,886
  • 1
  • 10
  • 27
0

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.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
0

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().

Pouyan
  • 363
  • 5
  • 22
0

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

ilkkachu
  • 6,221
  • 16
  • 30
-1

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.

cse
  • 4,066
  • 2
  • 20
  • 37