3

I'm sorry in advance because I'm fairly new to programming and some things in my code will probably look like utter nonsense! I'm not entirely sure if I'm using atoi right.

I'm trying to create a program that splits a user input sentence into single words and doubles the number(float/integer) if a user inputs one. For example, I have 3 cats would come out as:

I
have
6
cats

My program right now is able to split the sentence, but I can't get the integer to double. Can anyone help me with this?

Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main()
{
    char sentence[100];

    printf("Enter a sentence to split: ");
    scanf("%[^\n]s", sentence);
    char *pch;
    int y;

    y = atoi(sentence);
    printf("After splitting:\n", sentence);
    pch = strtok(sentence," ");
    while (pch != NULL) {
        printf("%s\n", pch);
        pch = strtok(NULL, " ");
    }
    system("PAUSE");
}

And my output so far:

Enter a sentence to split: Hi, I have 7 cats.
After splitting:
Hi,
I
have
7
cats.
Press any key to continue . . .
Bluth
  • 33
  • 5
  • Do you want a specific solution for this case or rather a generic one? – Sossenbinder Jan 23 '16 at 20:29
  • `atoi()` converts a string to an integer. You are calling it at the beginning of your program on the entire sentence, not on a single word (token). Do you really expect the entire sentence to be an integer, or just one word of it? Put your `atoi()` inside the loop that processes each word (token). Another thing to consider: How do you know if the token is an integer or not? See man [atoi](http://linux.die.net/man/3/atoi). – e0k Jan 23 '16 at 20:32
  • @Bluth, I would recommend you check my answer and say if this is what you need or something else. – Simply Me Jan 23 '16 at 20:44
  • Is an *integer* strictly a sequence of digits or are `+2`, `-3`, `0x20` OK? – chqrlie Jan 23 '16 at 21:02
  • @chqrlie, I believe I just need any number >= 1, decimals and all, if that makes sense. I apologize for not being specific about what I needed. – Bluth Jan 23 '16 at 21:51
  • @Bluth: you did mention *integer*, so decimals should not be OK. – chqrlie Jan 23 '16 at 22:02
  • @chqrlie, yeah sorry I just looked back at the example and realized there were decimals, I apologize. – Bluth Jan 23 '16 at 22:04
  • @Bluth: I updated the answer for the floating point case. – chqrlie Jan 23 '16 at 22:30

4 Answers4

1

Here is a simpler version with a test for all digit numbers:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
     char sentence[100];
     char *pch;

     printf("Enter a sentence to split: ");
     if (!fgets(sentence, sizeof sentence, stdin))
          return 1;

     printf("After splitting:\n");
     for (pch = strtok(sentence, " \n"); pch != NULL; pch = strtok(NULL, " \n")) {
         if (pch[strspn(pch, "0123456789")] == '\0') {
             printf("%d\n", atoi(pch) * 2);
         } else {
             printf("%s\n", pch);
         }
     }
     system("PAUSE");
     return 0;
}

If you want to parse floating point numbers too, you could use this code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

int main(void) {
     char sentence[100];
     char *pch, *pend;
     double value;

     printf("Enter a sentence to split: ");
     if (!fgets(sentence, sizeof sentence, stdin))
          return 1;

     printf("After splitting:\n");
     for (pch = strtok(sentence, " \n"); pch != NULL; pch = strtok(NULL, " \n")) {
         value = strtod(pch, &pend);
         if (*pend == '\0' && isfinite(value)) {
             printf("%g\n", value * 2);
         } else {
             printf("%s\n", pch);
         }
     }
     system("PAUSE");
     return 0;
}

Note the test for isfinite() to avoid recognizing inf and nan as numbers.

NOTE: isfinite is part of C99, it is not supported by VisualStudio 12, but more recent versions do support it. For this older version, use _finite() defined in <float.h>.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Thanks! I'm using VS 12 and I don't think it supports "isfinite()" as I've tried to add different libraries but can't seem to get it to work. Do you happen to know any way around this? – Bluth Jan 23 '16 at 22:56
  • `isfinite()` is part of Standard C. Did you include ``? – chqrlie Jan 23 '16 at 23:05
  • Yes I included it. I googled it and apparently it is not part of "" or "" on a version of VS 12. I'll try it on a different version and make sure it works for me. Thank you so much for your help! – Bluth Jan 23 '16 at 23:14
  • It is a bug in VisualStudio 12.0. see http://stackoverflow.com/questions/14580168/stdisfinite-on-msvc and https://connect.microsoft.com/VisualStudio/feedback/details/783412/isnan-and-isfinite-support-does-not-exist-in-math-h-or-cmath . For a specific fix, use `_finite(x)` defined in `` or upgrade your VisualStudio to a more recent version. – chqrlie Jan 23 '16 at 23:22
  • @Bluth: can you please upvote the answer you accepted? – chqrlie Jan 24 '16 at 19:54
0

For the split part I would recommend this version, it is easier to understand (considering that you've learned the for way of working). It does the same thing, but helps you organize your code.

char *p;
int i;
    for(p = strtok(sentence, " "); p != NULL; p = strtok(NULL, " "))
    {
        int isNumber = 1;
        for(i = 0; i < strlen(p); i ++)
        {
            if(!isDigit(p[i])
            {
                isNumber = 0;
                break;
            }
        }
        if(isNumber == 1)
        {
            int number = atoi(pch);
            number *= 2;
            printf("%d\n", number);
        }
        else
        {
            printf("%s\n", p);
        }
    }

For the number, I would recommend using the atoi function. Here you have a good reference.

To solve your problem, first of all, you need to check every single word you get. For example: You take it word by word and see if the "word" contains ONLY digits. If it contains only digits, you can use atoi to convert it to a number, multiply it by 2 and print the result.

On the other hand, if you find a char that is NOT a digit, you have letters or any other characters in your word, so it is not a word so you simply print that as it is.

Simply Me
  • 1,579
  • 11
  • 23
  • good idea to use a `for` loop. You should use `isdigit((unsigned char)p[i]))` -- there is a missing `)`. – chqrlie Jan 23 '16 at 20:54
  • Yes, but if the char overflows, it will take the module, if you use special chars. Thanks for the tip, it's a good observation. – Simply Me Jan 24 '16 at 13:05
  • no it is not supposed to do that, because it is defined for all values of `unsigned char` and the value `EOF`. Taking the module creates a collision between `EOF` and `'\377'`, typically `ÿ` in iso-latin1. The glibc uses an array of 384 entries to handle both signed and unsigned char arguments but this is still incorrect for `'\377'`, and it does not mask the int argument, so any sufficiently large argument will crash the program (I checked it does for 1000000). Darwin uses a less efficient approach with an explicit test: it considers all values outside `0..127` to have no properties. – chqrlie Jan 24 '16 at 19:48
0

Here is a working example, even if it's not really precise. Basically, we need to find out whether our current string is a number or not. In this case, I just tried to get the first element of the current string, and tried to determine if it's numeric or not.

Of course, if you want to be really sure, we need to iterate the string and check every char, if it is a number or not.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main()
{
    char sentence[100];

    printf("Enter a sentence to split: ");
    scanf("%[^\n]s", sentence);
    char * pch;
    int y;
    y = atoi(sentence);
    printf("After splitting:\n", sentence);
    pch = strtok (sentence," ");
    while (pch != NULL)
    {
        if(isdigit(pch[0]))
        {
            int number = atoi(pch);
            number *=2;
            printf("%d\n",number);
            pch = strtok (NULL, " ");
            continue;
        }
        printf("%s\n",pch);
        pch = strtok (NULL, " ");
    }

    system("PAUSE");
}
Sossenbinder
  • 4,852
  • 5
  • 35
  • 78
  • What if the user input is this: I have 7a cats? This will crash. pch[0] is digit, but a is not digit. – Simply Me Jan 23 '16 at 20:37
  • @SimplyMe Indeed. It depends on how robust the answer has to be for OP. I edited my post to consider this. – Sossenbinder Jan 23 '16 at 20:38
  • I doubt that your code would catch that. You need to check everything. – Simply Me Jan 23 '16 at 20:41
  • @SimplyMe: how would the program crash? it would just ignore the trailing `a`. – chqrlie Jan 23 '16 at 20:42
  • @SimplyMe No, it doesn't. But I gave OP a hint on how he could do it – Sossenbinder Jan 23 '16 at 20:42
  • @Sossenbinder: this should work, yet you could remove the initial `atoi(sentence)`, and it is better to pass an `unsigned char` to `isdigit()`, use `isdigit((unsigned char)pch[0])`. – chqrlie Jan 23 '16 at 20:44
  • @chqrlie, yes, it will ignore it, but 7a is not a number, so you shouldn't double it. It's to subjective. It depends mostly on what OP needs. – Simply Me Jan 23 '16 at 20:47
  • Hi guys, sorry for getting back so late! I appreciate all the help! I've added to my code and moved the "atoi" part into my loop just like you guys have shown in all of your replies. I looked up "isdigit()" and it won't work if a user inputs a float like 2.5 or something so how do I go about changing that? Also, @SimplyMe, you're right, I don't think 7a should be doubled. I believe I just need any number >= 1, decimals and all. I apologize for not being specific about what I needed. – Bluth Jan 23 '16 at 21:49
0

Here's another (more compact) answer:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int is_numeric(char *s)
{
    int i = 0;
    while (s[i] != '\0') {
        if (!isdigit(s[i]))
            return 0;
        ++i;
    }
    return 1;
}

int main()
{
    char sentence[255];
    char *pch;

    printf("Enter a sentence to split: ");
    if (!fgets(sentence, sizeof(sentence), stdin)) {
        return 1;
    }
    sentence[strcspn(sentence, "\n\r")] = 0; /* Strip newline */

    pch = strtok(sentence, " ");
    while (pch != NULL) {
        if (atoi(pch)) {
            printf("%d\n", 2 * atoi(pch));
        } else {
            printf("%s\n", pch);
        }
        pch = strtok(NULL, " ");
    }
    return 0;
    /* system("PAUSE"); */
}

The key is that atoi will return 0 for a non-numeric argument.

bpm
  • 105
  • 4
  • interesting! but it does not handle `7a`. You might also remove the definition for `y` and protect the buffer with `if (scanf("%99[^\n]", sentence) != 1) return 1;` – chqrlie Jan 23 '16 at 20:58
  • Another idea for a compact version with a test for valid numbers: `char *p; if (strtol(pch, &p, 10) && !*p)` – chqrlie Jan 23 '16 at 20:59