# Shortest program to split a string at non-digits without RegExps [closed]

EDIT: If you're using Lisp, I have given some guidelines at the bottom in counting bytes.

Objective: Make the shortest function that splits a string at non-digits and returns an array consisting of only digits in each string, without the use of any regular expressions. Leading zeroes are to be included in each string.

## Current Standings (separated in categories):

• C/C++/C#/Java: 68 (C) ....
• GolfScript/APL/J: 13 (APL)
• All others: 17 (Bash, uses tr), 24 (Ruby)

## Rules:

• The format must be as a function with a single string argument. Up to two additional arguments may be added if necessary for the proper return of the array (e.g. sh/csh/DOS Batch needs an extra variable reference to return, etc.).

• The primary function declaration doesn't count, and nor does importing other standard libraries. #includes, imports, and usings don't count. Everything else does. This does include #defines and helper functions. Sorry for the confusion. Refer to this as a helpful guide as to what does/does not count (written in C-style syntax)

// doesn't count toward total, may be omitted unless
// non-obvious, like half of Java's standard library.
#include &lt;stdio.h&gt;

import some.builtin.Class // doesn't count, see above

#define printf p // counts towards total

/* Any other preprocessor directives, etc. count. */

int i = 0; // counts

someFunction(); // counts

char[][] myMainSplitFunction(char[][] array) { // doesn't count
// Everything in here counts
return returnArray; // Even this counts.
} // doesn't count

/* Everything in here counts, including the declaration */
char[][] someHelperFunction(char[] string) {
// stuff
} // even this counts

• The output must be a string array or similar (Array lists in Java and similar are acceptable). Examples of accepted output: String[], char[][], Array<String>, List<String>, and Array (object).

• The array must contain only contain variable-length string primitives or string objects. No empty strings should be present in the return, with the exception below. Note: the strings are to contain a string of consecutive matches, such as the example input and output below.

• If there are no matches, then the function body should return null, an empty array/list, or an array/list containing an empty string.

• No external libraries allowed.

• DOS line endings count as one byte, not two (already covered in meta, but needs to be emphasized)

• And the biggest rule here: no regular expressions allowed.

This is a question, so shortest code wins. Good luck!

And here are some example inputs and outputs (with C-style escapes):

Input:  "abc123def456"
Output: ["123", "456"]

Input:  "aitew034snk582:3c"
Output: ["034", "582", "3"]

Input:  "as5493tax54\\430-52@g9.fc"
Output: ["5493", "54", "430", "52", "9"]

Input:  "sasprs]tore\"re\\forz"
Output: null, [], [""], or similar


## Guidelines for Lisp

Here's what does and doesn't count in Lisp dialects:

;;; Option 1
(defun extract-strings (a b) ; Doesn't count
(stuff) ;;; Everything in here counts
) ; Doesn't count
;;; Option 2
(defun extract-strings (string &aux (start 0) (end 0)) ; Doesn't count
(stuff) ;;; Everything in here counts
) ; Doesn't count.

All other lambdas fully count towards the byte count.

• Wasn't this asked before? Feb 23, 2014 at 6:02
• Yes, but I re-asked it on Meta and made substantial edits to it before posting it again here. Because of this, it shouldn't be classified as a duplicate (the other related one should be closed if not already). Feb 23, 2014 at 6:06
• Shouldn't your "golf" be posted as an answer? Feb 23, 2014 at 10:57
• Sorry, but -1 for disallowing GolfScript. All languages should be allowed. Feb 23, 2014 at 18:09
• @Doorknob That's true, but I also understand the OP's feelings. People should have a chance to compete even if they don't speak GolfScript, J, or APL (and I'm guilty of perusing the latter in these competitions.) Can you give a look at my proposal in the thread he linked to? Feb 23, 2014 at 19:45

# JavaScript 88

88 chars when not counting function n(x){}

function n(x){
y=[],i=0,z=t=''
while(z=x[i++])t=!isNaN(z)?t+z:t&&y.push(t)?'':t
if(t)y.push(t)
return y
}


# Racket 149

Golfed (according to the rules, only the length of the second line counts)

(define (extract-numeric-substrings s)
(let L([x(map string->number(string-split s""))])(set! x(dropf x false?))(if(null? x)x(let-values([(h t)(splitf-at x number?)])(cons(apply ~a h)(L t)
)))))


Ungolfed

(define (extract-numeric-substrings s)
(let L ([x (map string->number (string-split s ""))])
(set! x (dropf x false?))
(if (null? x)
x
(let-values([(h t) (splitf-at x number?)])
(cons (apply ~a h) (L t))))))


Results

(map extract-numeric-substrings '("abc123def456"
"aitew034snk582:3c"
"as5493tax54\\430-52@g9.fc"
"sasprs]tore\"re\\forz"))

'(("123" "456")
("034" "582" "3")
("5493" "54" "430" "52" "9")
())


## Javascript with lambdas, 84

extract=s=>[].reduce.call(s,(r,c)=>r+=(c!=+c?r[r.length-1]==" "?"":" ":c),"").trim().split(" ")


Tested in Firefox 27

## VB.NET, 87

Imports System.Console
Imports System.String
Imports System.Char
Imports System.StringSplitOptions

Module All
Public Function Parse(S As String) As String()
Return Join("",From C In S Select If(IsDigit(C),C," ")).Split({" "},RemoveEmptyEntries)
End Function

Public Sub Main()
For Each S As String In {"abc123def456", "aitew034snk582:3c", "as5493tax54\\430-52@g9.fc", "sasprs]tore\""re\\forz"}
WriteLine(Join(":", Parse(S)))
Next S
End Sub
End Module


According the rules, only content of parse function is calculated:

Return Join("",From C In S Select If(IsDigit(C),C," ")).Split({" "},RemoveEmptyEntries)


I am not counting the imports towards the bytecount, as per the scoring rules. The body of the code is a 1 liner (with semicolons, so there is actually no advantage other than looking more golfed), though I am not sure what to count and what not to count in the function definitions. This was originally longer, but I got inspiration from Coaumdio's and DigitalTrauma's bash solutions.

import Data.Char {-Not counted-}
i x|isDigit x=x;i _=' ';f=words.map i {-Fully Counted-}


i preserves digit characters, and replaces all other characters with space. f just maps i onto a string and then applies words, which splits a string into a list of substrings that were separated by arbitrarily long runs of whitespace. If I am missing any of the rules whereby I can reduce my bytecount by omitting certain characters from the score, I would appreciate comments to that effect.

APL: 22

{(a/1,2</a)⊂⍵/⍨a←⍵∊⎕D}


Explanation:

a←⍵∊⎕D creates boolean of argument (⍵) being a digit (system variable ⎕D contains '0123456789')

⍵/⍨a  takes just the numeric part of the argument

(a/1,2</a)⊂  makes substrings of numbers only, starting at first number found


# Pharo Smalltalk, 60

f:=[:s|(s splitOn:[:e|e isDigit not])reject:[:e|e isEmpty]]


Outputs:

f value:'abc123def456' -> OrderedCollection('123' '456')
f value:'aitew034snk582:3c' -> OrderedCollection('034' '582' '3')
f value:'as5493tax54\430-52@g9.fc' -> OrderedCollection('5493' '54' '430' '52' '9')
f value:'sasprs]tore\"re\forz' -> OrderedCollection()


# C (gcc), 50 bytes

Note that the TIO counts 48, that is because it doesn't count the global at the top

c;
void split(unsigned char *s, unsigned char **a) {
for(c=0;*s;s++)c=*s-48u>9?*s=0:c?:(*a++=s);*a=0;
}


Try it online!

Significant improvement of Coaumdio's algorithm which I would comment on, but the user hasn't been active since then. 😢

The most notable difference is that it uses unsigned char * instead of char *. This is because the function signature is free, and it allows us to do unsigned arithmetic instead of ctype. This doesn't affect the test cases, but if we were to have a character with the high bit set, it would.

for(c=0;*s;s++)


I moved the variable to a global variable with implicit int and initialized in the for loop. Saves 3 bytes.

Otherwise, this simply loops through s until it reaches the null terminator.

Now to explain this glorious hot garbage.

c=*s-48u>9?*s=0:c?:(*a++=s);


Let's format it a little better, convert it to an if-else, and step through it.

Overview of the logic:

if (*s - (unsigned)'0' > 9) { // c=*s-48u>9?
c = *s = '\0';            //   *s=0
} else if (c != 0) {          // :c?
c = c;                    //   // implicit: c
} else {                      // :
c = (int)(*a++ = s);      //   (*a++ = s)
}                             // ;

if (*s - (unsigned)'0' > 9) { // c=*s-48u>9?


Thanks to unsigned arithmetic, we can remove the isdigit call entirely.

Basically, what the subtraction does is an unsigned subtraction, with negatives being converted to really big positives.

So for example:

' ' = 32 - 48 = -16 = 4294967280 (for a 32-bit int)
'0' = 48 - 48 = 0 = 0
'9' = 54 - 48 = 9 = 9
'A' = 65 - 48 = 17 = 17


As you can see, '0' to '9' will be turned into 0-9, and anything else will be larger than that, so we can just check if the result of the subtraction is greater than 9.

    c = *s = '\0';            //   *s=0


If it is not a digit, we will replace the contents of *s with a '\0' byte. The result of this assignment makes the ternary return 0, also assigning c to 0.

} else if (c != 0) {          // :c?
c = c;                    //   // implicit: c


If it is a digit and c is set, we set it to itself using GCC's ?: extension, which evaluates to the condition itself for true if the middle of the ternary is left out.

} else {                      // :
c = (int)(*a++ = s);      //   (*a++ = s)
}                             // ;


Last, if it is a digit and c is not set, we will store s into a and advance it, and here is the disgusting part: We then use the result of that assignment to set c to a non-zero value...assigning an integer to a pointer. 🤢

*a=0;


Finally, we NULL terminate a and return.

C# 66

static char[] n(string s){return s.Where(Char.IsDigit).ToArray();}

• This is not a valid solution, I don't think the OP wants you to split on the empty string ;) Oct 21, 2014 at 15:40