Summing A String

June 19, 2020

In a string consisting of digits and other non-digit characters, the digits form an embedded series of positive integers. For instance, the string “123abc45def” contains the embedded integers 123 and 45, which sum to 168.

Your task is to write a program that takes a string and writes the sum of the integers embedded in the string. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

Pages: 1 2

22 Responses to “Summing A String”

  1. Paul said

    In Python.

    import re
    
    def sumstr(txt):
        return sum(int(n) for n in re.findall(r"(\d+)", txt))
    
    txt = "77Pro123gra34mming Pra987xis is fun32"
    print(sumstr(txt))  # --> 1253
    
  2. chaw said

    Here are two solutions in standard Scheme (R7RS). The first is a “one
    liner” that uses string-tokenize from SRFI 13. The second uses only
    simple Scheme. It also reliably (based on my reading of R7RS) handles
    numbers in character-sets other than ASCII. (See sample inputs.)

    (import (scheme base)
            (scheme write)
            (scheme char)
            (only (srfi 14) char-set:digit)
            (only (srfi 13) string-tokenize string-fold))
    
    (define samples ; ((input . output) ...)
      '(("123abc45def" . 168)
        ("" . 0)
        ("a-123bc45xyz" . 168)
        ("99bottles0corks" . 99)
        ("१२३कखग४५घङ" . 168)
        ("९९बाटल्या०बुच" . 99)))
    
    (define (string-numbers-sum str)
      (apply + (map string->number (string-tokenize str char-set:digit))))
    
    (define (string-numbers-sum/v2 str)
      (let ((len (string-length str)))
        (let loop ((i 0)
                   (pnum 0)
                   (s 0))
          (if (>= i len)
              s
              (let ((c (string-ref str i)))
                (if (char-numeric? c)
                    (loop (+ i 1)
                          (+ (* 10 pnum) (digit-value c))
                          s)
                    (loop (+ i 1)
                          0
                          (+ s pnum))))))))
    
    (define (demo f)
      (write (equal? (map cdr samples)
                     (map f (map car samples))))
      (newline))
    
    (demo string-numbers-sum)
    (demo string-numbers-sum/v2)
    

    Output:

    #t
    #t
    

  3. matthew said

    Here’s some Haskell. Scan the string accumulating digits, when a complete number is found, use base-10 arithmetic to add number into running total:

    import Data.Char
    
    getdigit c | isDigit c = Just(digitToInt(c)) | otherwise = Nothing
    
    adc 0 [] t = t
    adc carry [] t = adc 0 [carry] t
    adc carry s [] = adc carry [] s
    adc carry (m:s) (n:t) = mod : adc div s t where (div,mod) = divMod (carry+m+n) 10
    
    scan1 sum stack s = scan (adc 0 sum stack) [] s
    scan sum [] [] = reverse sum
    scan sum stack [] = scan1 sum stack []
    scan sum stack (Just d : s) = scan sum (d:stack) s
    scan sum stack (Nothing : s) = scan1 sum stack s
    
    main = print (scan [] [] (map getdigit "123abc45def")) -- [1,6,8]
    
  4. Globules said

    Here’s another Haskell version, using splitWhen from the “split” library.

    import Data.Char (isDigit)
    import Data.List.Split (splitWhen)
    
    sumstr :: (Integral a, Read a) => String -> a
    sumstr = sum . map read . filter (not . null) . splitWhen (not . isDigit)
    
    main :: IO ()
    main = do
      print $ sumstr "123abc45def"
      print $ sumstr "xy123abc45def"
      print $ sumstr ""
    
    $ ./sumstr
    168
    168
    0
    
  5. Daniel said

    Here’s a shell script solution. sed is combined with tr to replace repeated non-digit characters with a +. This could alternatively be achieved by passing sed‘s flag for extended regex, and checking for one or more non-digit characters.

    #!/usr/bin/env sh
    
    echo "0 $1 0" | sed 's/[^0-9]/+/g' | tr -s '+' | bc
    

    Example:

    $ ./sum.sh 123abc45def
    168
    $ ./sum.sh 
    0
    
  6. Daniel said

    Here’s a solution in C.

    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // Overflow is not checked/handled.
    int main(int argc, char* argv[]) {
      assert(argc == 2);
      int sum = 0;
      int multiplier = 1;
      int n = strlen(argv[1]);
      for (int i = n - 1; i >= 0; --i) {
        char c = argv[1][i];
        if (c < '0' || c > '9') {
          multiplier = 1;
          continue;
        }
        sum += multiplier * (int)(c - '0');
        multiplier *= 10;
      }
      printf("%d\n", sum);
      return EXIT_SUCCESS;
    }
    

    Example:

    $ ./a.out asdf123abc45def
    168
    
  7. matthew said

    @Daniel – nice solutions, scanning the string in reverse is a good idea, here’s a simplified version of my “elementary” Haskell solution:

    import Data.Char
    
    getdigit c | isDigit c = Just(digitToInt(c)) | otherwise = Nothing
    
    add 0 s = s
    add d [] = [d]
    add d (a:s) = mod : add div s where (div,mod) = divMod (a+d) 10
    scan1 [] stack s = scan [] (0:stack) s
    scan1 (d:sum) stack s = scan sum (d:stack) s
    scan sum stack (Just d : s) = scan1 (add d sum) stack s
    scan sum stack (Nothing : s) = scan ((reverse stack) ++ sum) [] s
    scan sum stack [] = (reverse sum) ++ stack
    
    main = print (scan [] [] (reverse (map getdigit "123abc450def"))) -- [1,6,8]
    
  8. matthew said

    I left an extra “0” in the test data there, so the result is actually “[5,7,3]”.

  9. Xero said

    Here’s a simple solution: get a list of numbers in the string, then add them up. A Commonlisp implementation:

    (defun split-string (str &key (pred #'(λ (c) (char= c #\space))))
      "return a list of substrings of str delimited at positions where pred is T.                                                                                                                 
    The charcters at the point where pred is T are not included"
      (do ((i (1- (length str)) (1- i))
           (current nil)
           (acc nil))
          ((< i 0) (or (and (null current) acc)
                       (cons (coerce current 'string) acc)))
        (let ((c (char str i)))
          (cond ((funcall pred c)
                 (unless (null current)
                   (push (coerce current 'string) acc))
                 (setf current nil))
                (t (push c current))))))
    
    (defun sum-numbers-in-string (str)
      "return the sum of the numbers in str where each number is contigous."
      (reduce #'(λ (a b) (+ a (parse-integer b)))
              (split-string str :pred #'alpha-char-p)
              :initial-value 0))
    
  10. matthew said

    A different approach in Python:

    >>> f = lambda s: eval(re.sub("[^0-9]+","+",s).strip("+"))
    >>> f("123abc45def")
    168
    
  11. matthew said

    Actually, that’s rather like Daniel’s shell solution.

  12. Tamil said

    Here is a different solution:

    #123abc45def
    st = input(“Enter string”)
    sum_val = 0
    msg = ”
    for i in st:
    if ord(i)>47 and ord(i)< 58:
    msg+=i
    else:
    if msg!=”:
    sum_val+=int(msg)
    msg=”
    else:
    continue
    print(sum_val)

  13. Maksym Fedenko said

    public class Exercises_2 {
    public static void main(String[] args) {
    class Input{
    public String inputString(){
    Scanner scanner = new Scanner(System.in);
    return scanner.nextLine();
    }
    public int string_in_int_out(String s){
    int sum = 0;
    char [] inputValue = s.toCharArray();
    char [] numbers = new char [inputValue.length + 1];
    for (int i = 0; i < inputValue.length; i++) {
    if (Character.isDigit(inputValue[i])){
    numbers[i] = inputValue[i];
    }else {
    continue;
    }
    }
    String buffer = “”;
    for (int i = 0; i < numbers.length; i++) {
    if (!Character.isDigit(numbers[i])){
    continue;
    }else if(Character.isDigit(numbers[i])){
    buffer += numbers[i];
    if (!Character.isDigit(numbers[i + 1])){
    sum += Integer.parseInt(buffer);
    buffer = “”;
    }
    }
    }
    return sum;
    }
    }
    Input input = new Input();
    System.out.println(input.string_in_int_out(input.inputString()));
    }
    }

  14. Maksym Fedenko said

    Hi guys how I can post my solutions like a code?

  15. Daniel said

    Here’s another solution in C.

    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    // Overflow is not checked/handled.
    int main(int argc, char* argv[]) {
      assert(argc == 2);
      long sum = 0;
      while (*argv[1]) {
        sum += strtol(argv[1], &argv[1], 10);
        if (*argv[1]) ++(argv[1]);
      }
      printf("%ld\n", sum);
      return EXIT_SUCCESS;
    }
    

    Example:

    $ ./a.out asdf123abc45def
    168
    
  16. V said

    A bit late to the party…here is a one liner in Ruby:

    puts ARGV.first.scan(/\d+/).map(&:to_i).sum
    

    Example:

    ruby sum-string.rb '123abc45def'
    168
    
  17. solution in Clojure with transducer:

    (ns stringsum.core
      (:require [clojure.string :as string]
                [clojure.edn :as edn])
      (:gen-class))
    
    (defn sum-numbers [str]
      (let [xf (comp (filter (complement string/blank?))
                     (map edn/read-string))]
        (transduce xf + 0 (string/split str #"[a-zA-Z]+"))))
    
  18. Although the regex should better be #”[^\d+]”

  19. Larry Lee said

    A naive Haskell solution

    strSum = snd . foldr (\c (n, x) -> if isDigit c then (n + 1, (digitToInt c) * 10^n + x) else (0, x)) (0, 0)
    
  20. Humberto Rondon said

    Here’s mine in Perl, using regexes

    use strict;
    use warnings;
    use List::Util qw(reduce);
    
    my $str = "123abc45def";
    
    print (reduce { $a + $b } ($str =~ /(\d+)/g));
    
  21. Hakan said
    
    if (!empty($_POST['string'])) {
    	$string = $_POST['string'];
    	
    	preg_match_all('/\d+/', $string, $matches);
    
    	$total = 0;
    	foreach ($matches as $match) {
    		foreach ($match as $integer) {
    			$total += $integer;
    		}
    	}
    
    	echo "<pre>";
    	var_dump($matches);
    
    	echo "The sum of all integers in the string that you have entered is: ".$total;
    
    }
    
    ?>
    
    <!DOCTYPE html>
    <html>
    <head>
    	<title>Summing strings</title>
    </head>
    <body>
    	<form action="SummingIntegersInString.php" method="POST">
    		<input name="string">
    		<button type="submit">Sum It!</button>
    	</form>
    </body>
    </html>
    
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: