A strict regular expression for matching chemical formulae

Viewed 1846

In the course of processing a large textual chemical database with Perl, I had been faced with the problem of using a regex to match chemical formulae. I have seen these two previous topics, but the suggested answers there are too loose for my requirements.

Specifically, my (admittedly limited) research has led me to this posting that gives a regex for the currently accepted chemical symbols, which I'll copy here for reference

[BCFHIKNOPSUVWY]|[ISZ][nr]|[ACELP][ru]|A[cglmst]|B[aehikr]|C[adeflos]|D[bsy]|Es|F[elmr]|G[ade]|H[efgos]|Kr|L[aiv]|M[cdgnot]|N[abdehiop]|O[gs]|P[abdmot]|R[abe-hnu]|S[bcegim]|T[abcehilms]|Xe|Yb

(Thus e.g. C, Cm, and Cn will pass, but not Cg or Cx.)

As with the previous questions, I also need to match numbers, complete sets of parentheses and complete sets of square brackets, so that both e.g. C2H6O and (CH3)2CFCOO(CH2)2Si(CH3)2Cl are matched.

So how do I combine the previous solutions with the grand regex for matching valid chemical elements to strictly match a chemical formula?

(If it's not too much trouble to add, a blow-by-blow account of how to humanly parse the regex would be appreciated greatly, though not strictly necessary.)

3 Answers

Since this post is a top result for "regex chemistry symbols", I would also like to submit a solution. It is a python script that uses a regex to match chemistry formulas of the type A#B#, where A and B are chemical symbols and # is a number. The script as implemented matches and then surrounds the matches with \ce{} for use in LaTeX. It also includes the ability to exclude matches if the capture is in a user-defined list, which means words such as "I" and "In" will not be matched. Gist Link.

#!/usr/bin/env python3
# Find chemical symbols and surround them with \ce{ Symbol }
# Problem words: I, HOW, In, degrees K. Add words to exlist to ignore them.

import re, sys

if len(sys.argv) < 2 :
  print('Usage:> {} <filename>'.format(sys.argv[0]))
  sys.exit(1)

ptable =" H                                                                  He "
ptable+=" Li  Be                                         B   C   N   O   F   Ne "
ptable+=" Na  Mg                                         Al  Si  P   S   Cl  Ar "
ptable+=" K   Ca  Sc  Ti  V   Cr  Mn  Fe  Co  Ni  Cu  Zn Ga  Ge  As  Se  Br  Kr "
ptable+=" Rb  Sr  Y   Zr  Nb  Mo  Tc  Ru  Rh  Pd  Ag  Cd In  Sn  Sb  Te  I   Xe "
ptable+=" Cs  Ba  La  Hf  Ta  W   Re  Os  Ir  Pt  Au  Hg Tl  Pb  Bi  Po  At  Rn "
ptable+=" Fr  Ra  Ac  Rf  Db  Sg  Bh  Hs  Mt  Ds  Rg  Cn Nh  Fl  Mc  Lv  Ts  Og "
ptable+="             Ce  Pr  Nd  Pm  Sm  Eu  Gd  Tb  Dy Ho  Er  Tm  Yb  Lu     "
ptable+="             Th  Pa  U   Np  Pu  Am  Cm  Bk  Cf Es  Fm  Md  No  Lr     "

exlist = ['C','I','In','K','HOW'] # exclude these words from being replaced
orsyms = '|'.join(ptable.split())
resyms = re.compile(r'\b'+'((?:(?:{})\d*)+)'.format(orsyms)+r'\b')

latexfile=sys.argv[1]
with open(latexfile,'r') as fd:
    for line in fd:

        for m in list(set(resyms.findall(line))):
            if m not in exlist :
                line = re.sub(r'\b'+m+r'\b', r'\ce{'+m+r'}', line)

        print(line,end='')
Related