-2

I am currently trying to brush up on Java for university using codebat. My goal is to take a string of any ASCII characters, split all of the numbers from the string, then return the sum of all of the numbers as an int.

For example: foo("abc123xyz") should return 123 and foo("12cd43ad") should return 55

Here is my code:

public int sumNumbers(String str) {
  int sum = 0;
  
  String[] numArr = str.split("\\D+"); //This is my attempted regex
  for (String num: numArr) {
    sum += Integer.parseInt(num);
  }
  
  return sum;
}

When I run sumNumbers("abc123xyz") or sumNumbers("aa11b33"), I get this error:

NumberFormatException: For input string: ""

Why is there an empty string in my numArr, and what is the proper regex to solve this issue?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
bulldogs586
  • 69
  • 1
  • 4
  • Because you split with non-digits as separator, and this separator occurs at the beginning of the string. This behavior is also described in the documentation. Simple fix: wrap in an `if` statement: `if (!num.isEmpty()) { ... }`. – MC Emperor Nov 28 '20 at 09:28

4 Answers4

3

Matcher is more applicable for this purpose then split:

int sum = 0;
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(str);
while(m.find()) {
    sum+=Integer.parseInt(m.group());
}
return sum;
Qwer Izuken
  • 599
  • 2
  • 6
1

Check the following lines from the documentation of String#split:

When there is a positive-width match at the beginning of this string then an empty leading substring is included at the beginning of the resulting array. A zero-width match at the beginning however never produces such empty leading substring.

You can confirm it by printing the resulting array. An empty string can not be parsed into an int. A workaround is to ignore the strings which can not be parsed into an int.

public class Main {
    public static void main(String[] args) {
        // Tests
        System.out.println(sumNumbers("abc123xyz"));
        System.out.println(sumNumbers("12cd43ad"));
    }

    public static int sumNumbers(String str) {
        int sum = 0;

        String[] numArr = str.split("\\D+");
        //System.out.println(Arrays.toString(numArr));
        for (String num : numArr) {
            try {
                sum += Integer.parseInt(num);
            } catch (NumberFormatException e) {
                // Ignore the strings which can not be parsed into int
            }
        }

        return sum;
    }
}

Output:

123
55
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

You are splitting on the non-digits, unless the first character is a digit you're going to get a blank string, because it's the correct behavior according to the java doc https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#split-java.lang.String-int-

Your function works fine on strings with digits as first characters.

DonTomato
  • 21
  • 2
0

It may be needed to filter out the empty values after the split("\\D+") by non-digit sequences.

Also, stream API may be used to calculate the sum:

public static int sumNumbers(String str) {
    return Arrays.stream(str.split("\\D+"))   // Stream<String>
                 .filter(s -> !s.isEmpty())   // remove empty strings 
                 .mapToInt(Integer::parseInt) // get IntStream (stream of primitive int)
                 .sum();
}

Test:

System.out.println(sumNumbers("abc123z-3yx"));   // 123 + 3
System.out.println(sumNumbers("12abc34z-6yx"));  // 12 + 34 + 6

Output:

126
52

Similarly, a pattern to match digit sequences "-?\\d+" (including optional -? to handle negative values) may be used:

public static int sumNumbers(String str) {
    Pattern p = Pattern.compile("-?\\d+");
    return p.matcher(str)
            .results()                   // Stream<MatchResult> since Java 9
            .map(MatchResult::group)     // get the matched sequence as string
            .mapToInt(Integer::parseInt) // get primitive int
            .sum();
}

Results for the same test data: (123 - 3) and (12 + 34 - 6)

120
40
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42