Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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.

share|improve this question

5 Answers 5

up vote 14 down vote accepted

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]*"]
]
share|improve this answer
    
Very nice, simple and elegant. +1 –  RunnyKine Aug 28 at 17:38
    
@RunnyKine Thanks! :-) Hopefully it makes up for my earlier blunder. –  Mr.Wizard Aug 28 at 17:50
    
It does :). And I really like the RegEx version too. –  RunnyKine Aug 28 at 17:55
    
Patterns blow my mind. –  s0rce Aug 28 at 20:39
    
@s0rce Thanks for the Accept. –  Mr.Wizard Aug 29 at 2:16

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.

share|improve this answer
    
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. –  Mr.Wizard Aug 29 at 2:15
1  
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. –  WReach Aug 29 at 5:01

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"}
share|improve this answer
elements = SortBy[ElementData[#, "Abbreviation"] & /@ ElementData[], Minus@*StringLength];
StringCases["Fe3O2", DigitCharacter .. | elements]

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

(Thanks to Mr.Wizard for syntax improvements.)

share|improve this answer
    
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. –  Mr.Wizard Aug 28 at 19:15
    
@Mr.Wizard Thanks, I like your version better so I incorporated it in the answer. –  Pickett Aug 28 at 19:29
    
+1 for @*, there is always something new to learn! –  ybeltukov Aug 28 at 20:29

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

share|improve this answer
    
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. :^) –  Mr.Wizard Aug 28 at 18:41
    
@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 :) –  ybeltukov Aug 28 at 19:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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