1

I am solving this problem:

Given a string str containing alphanumeric characters, calculate sum of all numbers present in the string.

Input:

The first line of input contains an integer T denoting the number of test cases. Then T test cases follow. Each test case contains a string containing alphanumeric characters.

Output:

Print the sum of all numbers present in the string.

Constraints:

1 <= T<= 105

1 <= length of the string <= 105

Example: Input:

4
1abc23
geeks4geeks
1abc2x30yz67
123abc

Output:

24
4
100
123

I have come up with the following solution:

#include <stdio.h>
#include <string.h>
int main() {
    //code
int t,j;
char a[100000];
scanf("%d",&t);
 while(t--)
 {   
    int sum=0,rev=0,i=0,l;
    scanf("%s",a); 
    l=strlen(a);
    for(i=0;i<l;i++) 
    {
          if (isdigit(a[i])){
             while(isdigit(a[i])){
               rev = rev *10 + (a[i]-48);
               i++;
             }
          }
        sum+=rev;
        rev=0;        
    }
    printf("%d\n",sum);
 }
    return 0;
}

This code is working fine. BUT if loop termination condition is changed from i < l to a[i]!='\0', then code doesn't work. Why?

Community
  • 1
  • 1
Laveena
  • 97
  • 1
  • 10

3 Answers3

2

You will have to change the logic in your while loop as well if you wish to change that in your for loop condition because it's quite possible number exists at the end of the string as well, like in one of your inputs 1abc2x30yz67. So, correct code would look like:

Snippet:

 for(i=0;a[i]!='\0';i++) 
    {
          if (isdigit(a[i])){
             while(a[i]!='\0' && isdigit(a[i])){ // this line needs check as well
               rev = rev *10 + (a[i]-48);
               i++;
             }
          }
        sum+=rev;
        rev=0;        
    }

On further inspection, you need the condition of i < l anyways in your while loop condition as well.

while(i < l && isdigit(a[i])){

Update #1:

To be more precise, the loop while(isdigit(a[i])){ keeps going till the end of the string. Although it does not cause issues in the loop itself because \0 ain't a digit, but a[i] != '\0' in the for loop condition let's you access something beyond the bounds of length of the string because we move ahead 1 more location because of i++ in the for loop whereas we already reached end of the string inside the inner while loop.

Update #2:

You need an additional check of a[i] == '\0' to decrement i as well.

#include <stdio.h>
#include <string.h>
int main() {
    //code
int t,j;
char a[100000];
scanf("%d",&t);
 while(t--)
 {   
    int sum=0,rev=0,i=0,l;
    scanf("%s",a); 
    l=strlen(a);
    for(i=0;a[i]!='\0';i++) 
    {
          if (isdigit(a[i])){
             while(a[i] != '\0' && isdigit(a[i])){ // this line needs check as well
               rev = rev *10 + (a[i]-48);
               i++;
             }
          }

        if(a[i] == '\0') i--; // to correctly map the last index in the for loop condition
        sum+=rev;
        rev=0;        
    }
    printf("%d\n",sum);
 }
    return 0;
}

Update #3:

You can completely avoid the while loop as well as shown below:

#include <stdio.h>
#include <string.h>
int main() {
    //code
int t,j;
char a[100005];
scanf("%d",&t);
 while(t--)
 {   
    int sum=0,rev=0,i=0,l;
    scanf("%s",a); 
    l=strlen(a);
    for(i=0;i<l;i++) {
      if (isdigit(a[i])){
         rev = rev * 10 + (a[i]-48);
      }else{
        sum += rev;
        rev = 0;
      }
    }
    printf("%d\n",sum + rev); // to also add last rev we captured
 }
    return 0;
}
nice_dev
  • 17,053
  • 2
  • 21
  • 35
  • Didn't get what you mean by "number at the end of the string as well". Number at the end of string will be a part of the string after that only we will have '\0'. Also I just added the mentioned condition and it's still not working.. – Laveena Feb 11 '20 at 13:58
  • @Laveena _it's still not working_ Can you share a demo link ? – nice_dev Feb 11 '20 at 14:07
  • I guess you forgot to add a[i]!= '\0' in your first link above. I am solving a question on geeksforgeeks so there are many testcase which the code might fail. so I am providing link here you can check your code there. Thanks.https://practice.geeksforgeeks.org/problems/sum-of-numbers-in-string/0 – Laveena Feb 11 '20 at 14:17
  • 1
    @Laveena Updated my answer. – nice_dev Feb 11 '20 at 14:29
  • so for loop never got it's terminating condition fulfilled. But won't it lead to an infinite for loop and hence runtime error. – Laveena Feb 13 '20 at 06:33
  • 1
    @Laveena Yes, a segmentation fault. – nice_dev Feb 13 '20 at 07:09
  • actually it was not giving any error so I thought code is fine. The ouput for all the input were coming to be 0. – Laveena Feb 13 '20 at 09:19
  • also if I am replacing %s with "%[^\n] the code give 0 as output. But does work if I put " %[^\n]". Can you tell me why so? – Laveena Feb 13 '20 at 09:31
  • @Laveena What's the difference? Additional space? You can find what `[^\n]` means here => https://stackoverflow.com/questions/39431924/what-does-n-mean-in-c – nice_dev Feb 13 '20 at 09:50
  • yeah additional space is the difference. I have searched for difference in "%[^\n]" & " %[^\n]" which is that the former doesn't takes spaces while the later can take space as input. But why I am getting 0 as output is still not clear to me. – Laveena Feb 14 '20 at 12:05
2

I would loop backwards over the string. No nested loops. Just take the 10s exponent as you move left

You have the length of the string, so there should be no reason to check for NUL char yourself

(untested code, but shows the general idea)

#include <math.h>


l=strlen(a);
int exp; 
exp = 0;
for(i = l-1; i >= 0; i--) 
{
      if (isdigit(a[i])) {
           rev = a[i]-48;  // there are better ways to parse characters to int 
           rev = (int) pow(10, exp) * rev;
           sum += rev;  // only add when you see a digit 
      } else { exp = -1; } // reset back to 10^0 = 1 on next loop 

    exp++;
}

Other solutions include using regex to split the string on all non digit characters, then loop and sum all numbers

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • THANKS! that is great. I would like to know what are those better ways to parse characters to int and also my doubt is still not clear why the mentioned code is not working. So, would be glad if you can help with that. – Laveena Feb 11 '20 at 14:26
  • 1
    Refer strtol function in the standard library – OneCricketeer Feb 11 '20 at 14:27
  • 1
    You can also move first to last to avoid loops. Also, shouldn't `i` go from `l-1` till `0`? – nice_dev Feb 11 '20 at 14:32
  • 1
    @vivek it's harder to track the tens places that way. And thanks, missed that – OneCricketeer Feb 11 '20 at 14:33
  • @cricket_007 It still needs to be `i>=0`. Also, you wouldn't need `pow(10, exp)` if you go from first to last. – nice_dev Feb 11 '20 at 14:34
  • @vivek I'm not sure I follow how you'd separate adding 3 vs 30 vs 30000 then? – OneCricketeer Feb 11 '20 at 14:38
  • 2
    *there are better ways to parse characters to int* Note that if `a[i]` is already known to be a digit, `a[i] - '0'` is guaranteed to give its value no matter what the character set in use is. – Andrew Henle Feb 11 '20 at 17:14
0

Other answers have pointed out the correct loop conditions to ensure proper operation of your program.

If you are allowed to use library functions other than isdigit, I would recommend using strtol with the EndPtr parameter (output parameter that points to the character in the string that caused strtol to stop scanning a number):

char str[] = "1abc23def5678ikl";

int main()
{
    char *pStop = str;
    int n, accum = 0;
    size_t len = strlen(str);

    do
    {
        n = strtol(pStop, &pStop, 10);
        pStop++;
        if(n)
        {
            printf("%d\n", n);
            accum += n;
        }
    }
    while(pStop < &str[len]);

    printf("Total read: %d\n", accum);

    return 0;
}
Govind Parmar
  • 20,656
  • 7
  • 53
  • 85