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$
  • $\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 Jul 5 '12 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$ – J. M.'s discontentment Jul 5 '12 at 4:23
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
| improve this answer | |
$\endgroup$
  • $\begingroup$ This works, but can you explain how it works please? $\endgroup$ – CHM Jul 5 '12 at 0:51
  • $\begingroup$ @CHM, it splits the strings where it finds a NumberString, keeping the NumberString $\endgroup$ – Rojo Jul 5 '12 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 Jul 5 '12 at 4:22
  • $\begingroup$ +1 -- however, I think you shouldn't need StringTrim after using StringSplit[string]. $\endgroup$ – Mr.Wizard Jul 5 '12 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 Jul 5 '12 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...

| improve this answer | |
$\endgroup$
  • $\begingroup$ One could of course use a StringExpression[] instead of a RegularExpression[]: s : (_ ~~ DigitCharacter .. ~~ _) :> s. $\endgroup$ – J. M.'s discontentment Jul 5 '12 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 Jan 22 '13 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.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Instead of the pattern [A-Za-z] in the regex, you could use [[:alpha:]]. $\endgroup$ – J. M.'s discontentment Jul 5 '12 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 Jul 8 '12 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

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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