5
$\begingroup$

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?

$\endgroup$
2
  • $\begingroup$ In my view using regular expression is not good, in the sense that use of regular expression degrades the performance, it take more time. $\endgroup$
    – user1674
    Commented Jul 5, 2012 at 4:14
  • $\begingroup$ @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. $\endgroup$ Commented Jul 5, 2012 at 4:23

4 Answers 4

12
$\begingroup$

What about this?

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
$\endgroup$
9
  • $\begingroup$ This works, but can you explain how it works please? $\endgroup$
    – CHM
    Commented Jul 5, 2012 at 0:51
  • $\begingroup$ @CHM, it splits the strings where it finds a NumberString, keeping the NumberString $\endgroup$
    – Rojo
    Commented Jul 5, 2012 at 0:52
  • $\begingroup$ 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). $\endgroup$
    – kglr
    Commented Jul 5, 2012 at 4:22
  • $\begingroup$ +1 -- however, I think you shouldn't need StringTrim after using StringSplit[string]. $\endgroup$
    – Mr.Wizard
    Commented Jul 5, 2012 at 9:02
  • $\begingroup$ @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 $\endgroup$
    – Rojo
    Commented Jul 5, 2012 at 12:25
6
$\begingroup$

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...

$\endgroup$
2
  • $\begingroup$ One could of course use a StringExpression[] instead of a RegularExpression[]: s : (_ ~~ DigitCharacter .. ~~ _) :> s. $\endgroup$ Commented Jul 5, 2012 at 4:06
  • $\begingroup$ StringSplit["there are 1234 words and numbers 5678 in here $999", RegularExpression["\\$?(\\d)+"] -> "$0"] // StringTrima little bit more compact $\endgroup$
    – Murta
    Commented Jan 22, 2013 at 0:22
3
$\begingroup$

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.

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

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:

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.