# Creating lists from a string, separating words from numbers

Say I have a string that contains numbers and words, such as this one:

string = "there are 1234 words and numbers 5678 in here $999";  How would I separate the string into an ordered list containing sublists populated with words and numbers? The ideal list would look like this: idealList = {{there are}, {1234}, {words and numbers}, {5678}, {in here}, {$999}}


I know how to extract all words and all numbers, but I can't create a list like the previous one.

Here's an example of what I tried to extract words and its output:

StringCases[string, RegularExpression["\\w(?<!\\d)[\\w'-]*"]]
{there, are, words, and, numbers, in, here}


I can also do this with pattern-matching instead of RegEx, but it doesn't get me closer to my goal.

Is my regex simply wrong, or does this problem require a tiny bit more involved solution?

-
In my view using regular expression is not good, in the sense that use of regular expression degrades the performance, it take more time. –  user1674 Jul 5 '12 at 4:14
@gaurab: in the absence of concrete evidence (e.g. timing results) to back up your claim, your words are far from being an answer. I have thus turned your "answer" into a comment. –  Ｊ. Ｍ. Jul 5 '12 at 4:23

StringSplit[string, i : NumberString :> i]


Ok, everyone's giving answers that actually work with the $, so here's an edit, as @kguler and @MrWizard suggested StringSplit[string, i : ("" | "$" ~~ NumberString) :> i] // StringTrim

-
This works, but can you explain how it works please? –  CHM Jul 5 '12 at 0:51
@CHM, it splits the strings where it finds a NumberString, keeping the NumberString –  Rojo Jul 5 '12 at 0:52
You can also modify the pattern to use Alternatives as in StringSplit[string, i : NumberString | StringExpression["$" ~~ NumberString] :> i] or, StringSplit[string, i : NumberString | ("$" ~~ NumberString) :> i] (+1). –  kguler Jul 5 '12 at 4:22
+1 -- however, I think you shouldn't need StringTrim after using StringSplit[string]. –  Mr.Wizard Jul 5 '12 at 9:02
@kguler, I edited. I had first tried that but fast and was bitten by ~~ vs |'s precedence and didn't spend a second realising that was the issue –  Rojo Jul 5 '12 at 12:25

Note that Rojo's solution splits the expression containing the dollar sign as well:

StringSplit["there are 1234 words and numbers 5678 in here $999", i : NumberString :> i] {"there are ", "1234", " words and numbers ", "5678", " in here$", "999"}


If you don't want that splitting to happen, here's one way, using a regex:

StringSplit["there are 1234 words and numbers 5678 in here $999", s : RegularExpression[".(\\d+)."] :> s] {"there are", " 1234 ", "words and numbers", " 5678 ", "in here ", "$999"}


If the spaces in the ends of the strings are bothersome, you can use StringTrim[] to get rid of them:

StringSplit["there are 1234 words and numbers 5678 in here $999", s : RegularExpression[".(\\d+)."] :> s] // StringTrim {"there are", "1234", "words and numbers", "5678", "in here", "$999"}


As another example:

str1 = "At 50x magnification, they'd better be paying me $1080 in 9 installments!"; StringSplit[str1, s : RegularExpression[".(\\d+)."] :> s] // StringTrim {"At", "50x", "magnification, they'd better be paying me", "$1080", "in",
"9", "installments!"}


The other methods presented would perform a splitting like

{"At", "50", "x magnification, they'd better be paying me", "$1080", "in", "9", "installments!"}  which may or may not be the desired behavior... - One could of course use a StringExpression[] instead of a RegularExpression[]: s : (_ ~~ DigitCharacter .. ~~ _) :> s. – Ｊ. Ｍ. Jul 5 '12 at 4:06 StringSplit["there are 1234 words and numbers 5678 in here$999", RegularExpression["\\$?(\\d)+"] -> "$0"] // StringTrima little bit more compact –  Murta Jan 22 '13 at 0:22

I prefer the solutions given by Rojo and J.M. to the following one. But if you want to see a working version of your original approach with StringCases and RegularExpression, here is one possibility

StringCases[string, RegularExpression["([A-Za-z]|\\s)+|(\\$|\\d)+"]]  It returns {"there are ", "1234", " words and numbers ", "5678", " in here ","$999"}


and as J.M. suggests above, apply StringTrim if desired. Handling decimals could also easily be added.

-
Instead of the pattern [A-Za-z] in the regex, you could use [[:alpha:]]. –  Ｊ. Ｍ. Jul 5 '12 at 3:57
Ah nice, I couldn't think of a more proper RegEx. Only ick is that there are spaces before and after " words and numbers ". –  CHM Jul 8 '12 at 16:32

Further variations:

using StringReplace:

List @@ StringTrim /@ StringReplace[string,
a : Except[{"$", DigitCharacter}] .. | NumberString | ("$" ~~ NumberString) :> {a}]


or, using the same replacement rule in StringCases:

 StringTrim /@ StringCases[string,
a : Except[{"$", DigitCharacter}] .. | NumberString | ("$" ~~ NumberString) :> {a}]


both yield:

-