9
$\begingroup$

I'm trying to split the strings of chemicals into their elements and numbers.

See this example

"Fe3O4" will be split into {"Fe","3","O","4"}

I've tried using StringSplit and various _LowerCaseQ type patters but it isn't working. I've also tried using StringSplit[#,""] to split everything and then finding the lower case characters and putting it back together but I haven't got it to work. Any solution would be greatly appreciated.

$\endgroup$

5 Answers 5

15
$\begingroup$

I propose:

StringCases[
  {"Fe3O4", "CO", "MgO", "Uut14AuO6"},
  DigitCharacter .. | (_?UpperCaseQ ~~ ___?LowerCaseQ)
]
{{"Fe", "3", "O", "4"}, {"C", "O"}, {"Mg", "O"}, {"Uut", "14", "Au", "O", "6"}}

Or as a RegularExpression:

StringCases[
  {"Fe3O4", "CO", "MgO", "Uut14AuO6"},
  RegularExpression["\\d+|[A-Z][a-z]*"]
]
$\endgroup$
5
  • $\begingroup$ Very nice, simple and elegant. +1 $\endgroup$
    – RunnyKine
    Commented Aug 28, 2014 at 17:38
  • $\begingroup$ @RunnyKine Thanks! :-) Hopefully it makes up for my earlier blunder. $\endgroup$
    – Mr.Wizard
    Commented Aug 28, 2014 at 17:50
  • $\begingroup$ It does :). And I really like the RegEx version too. $\endgroup$
    – RunnyKine
    Commented Aug 28, 2014 at 17:55
  • $\begingroup$ Patterns blow my mind. $\endgroup$
    – s0rce
    Commented Aug 28, 2014 at 20:39
  • $\begingroup$ @s0rce Thanks for the Accept. $\endgroup$
    – Mr.Wizard
    Commented Aug 29, 2014 at 2:16
5
$\begingroup$

I prefer one of @Mr.Wizard's solutions based on StringCases, but here is a solution using StringSplit:

StringSplit["Fe3O4", RegularExpression["(?=[A-Z]|\\d)"]]
(* {"Fe", "3", "O", "4"} *)

It splits the string at any position that is followed by an upper case letter or a digit. If multiple digits are possible:

StringSplit["Fe23O42", RegularExpression["(?=[A-Z]|(?<!\\d)\\d)"]]
(* {"Fe", "23", "O", "42"} *)

This is the same except that now a digit may not be preceded by another digit.

$\endgroup$
2
  • $\begingroup$ A powerful use of regular expressions. Is it possible to write this as a StringExpression? I can't think of a way at the moment. $\endgroup$
    – Mr.Wizard
    Commented Aug 29, 2014 at 2:15
  • 1
    $\begingroup$ No, regex is the only way to express look-ahead or look-behind conditions. A shame, really, because regex patterns are ugly as sin... but it sure is nice to have the PCRE engine to draw upon for those tougher jobs. $\endgroup$
    – WReach
    Commented Aug 29, 2014 at 5:01
3
$\begingroup$

You can use

chemSplit[s_String] := 
 Module[{pos = StringPosition[s, {_?UpperCaseQ, NumberString}, Overlaps -> False][[All, 1]]},
  StringSplit@StringInsert[s, " ", pos]
 ]

chemSplit["Fe3O4"]
{"Fe", "3", "O", "4"}
$\endgroup$
3
$\begingroup$
elements = SortBy[ElementData[#, "Abbreviation"] & /@ ElementData[], Minus@*StringLength];
StringCases["Fe3O2", DigitCharacter .. | elements]

{"Fe", "3", "O", "2"}

(Thanks to Mr.Wizard for syntax improvements.)

$\endgroup$
3
  • $\begingroup$ There is a precision about this that I like, and I can see the effort that you put into it. +1 One could replace the second part with: StringCases[string, DigitCharacter .. | elements] if desired. Also, these string functions are set up to work with lists of patterns so I believe you can drop the Apply[Alternatives, part. $\endgroup$
    – Mr.Wizard
    Commented Aug 28, 2014 at 19:15
  • $\begingroup$ @Mr.Wizard Thanks, I like your version better so I incorporated it in the answer. $\endgroup$
    – C. E.
    Commented Aug 28, 2014 at 19:29
  • $\begingroup$ +1 for @*, there is always something new to learn! $\endgroup$
    – ybeltukov
    Commented Aug 28, 2014 at 20:29
2
$\begingroup$

Just to be different

f = Flatten[List @@@ WolframAlpha["formula " <> #, "Result"][[1, 1]]] &;
f /@ {"Fe2O3", "MgO"}

{{"Fe", 2, "O", 3}, {"Mg", "O"}}

This approach seems to be stupid, but it can be easily extended to another chemical data (e.g. molar mass).

$\endgroup$
2
  • $\begingroup$ ybeltukov I am happy to see you posting again! Unfortunately you fell into the same trap I did; see the edit history of my answer. :^) $\endgroup$
    – Mr.Wizard
    Commented Aug 28, 2014 at 18:41
  • $\begingroup$ @Mr.Wizard I also happy to join you again! When I try to correct my answer I come to your answer exactly. I propose another approach instead of deleting the post :) $\endgroup$
    – ybeltukov
    Commented Aug 28, 2014 at 19:06

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.