用于匹配化学式的严格正则表达式

时间:2017-09-13 14:34:03

标签: regex perl pcre

在使用Perl处理大型文本化学数据库的过程中,我遇到了使用正则表达式匹配化学公式的问题。我之前看过these two个主题,但建议的答案对我的要求太宽松了。

具体来说,我的(公认有限的)研究让我this posting给出了目前公认的化学符号的正则表达式,我将在此复制以供参考

[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

(例如CCmCn将通过,但不会CgCx。)

与前面的问题一样,我还需要匹配数字,完整的括号和完整的方括号组,以便例如<{1}}和C2H6O匹配。

那么如何将以前的解决方案与大正则表达式相结合,以匹配有效的化学元素,以严格匹配化学式?

(如果添加起来并不太麻烦,那么如何人工解析正则表达式的详细描述将会受到极大的欢迎,尽管不是绝对必要的。)

3 个答案:

答案 0 :(得分:6)

我决定为什么不创建一个大规模的正则表达式来做你想要的(但仍然保持一个干净的正则表达式)。此正则表达式将与循环结合使用,以检查括号或括号组的匹配。

假设

我假设以下情况,因为OP没有给出正面和负面匹配的完整列表:

  • 嵌套括号不可能
  • 嵌套的方括号是不可能的
  • 围绕单个括号组的方括号组是多余的,因此不正确
  • 方括号组必须至少包含2个组,其中1个组必须是括号组

如果这些假设中的任何一个不正确,请告诉我,以便我可以相应地修复正则表达式

答案

<强> View this regex in use here

代码

(?(DEFINE)
  (?# Periodic elements )
  (?<Hydrogen>H)
  (?<Helium>He)
  (?<Lithium>Li)
  (?<Beryllium>Be)
  (?<Boron>B)
  (?<Carbon>C)
  (?<Nitrogen>N)
  (?<Oxygen>O)
  (?<Fluorine>F)
  (?<Neon>Ne)
  (?<Sodium>Na)
  (?<Magnesium>Mg)
  (?<Aluminum>Al)
  (?<Silicon>Si)
  (?<Phosphorus>P)
  (?<Sulfur>S)
  (?<Chlorine>Cl)
  (?<Argon>Ar)
  (?<Potassium>K)
  (?<Calcium>Ca)
  (?<Scandium>Sc)
  (?<Titanium>Ti)
  (?<Vanadium>V)
  (?<Chromium>Cr)
  (?<Manganese>Mn)
  (?<Iron>Fe)
  (?<Cobalt>Co)
  (?<Nickel>Ni)
  (?<Copper>Cu)
  (?<Zinc>Zn)
  (?<Gallium>Ga)
  (?<Germanium>Ge)
  (?<Arsenic>As)
  (?<Selenium>Se)
  (?<Bromine>Br)
  (?<Krypton>Kr)
  (?<Rubidium>Rb)
  (?<Strontium>Sr)
  (?<Yttrium>Y)
  (?<Zirconium>Zr)
  (?<Niobium>Nb)
  (?<Molybdenum>Mo)
  (?<Technetium>Tc)
  (?<Ruthenium>Ru)
  (?<Rhodium>Rh)
  (?<Palladium>Pd)
  (?<Silver>Ag)
  (?<Cadmium>Cd)
  (?<Indium>In)
  (?<Tin>Sn)
  (?<Antimony>Sb)
  (?<Tellurium>Te)
  (?<Iodine>I)
  (?<Xenon>Xe)
  (?<Cesium>Cs)
  (?<Barium>Ba)
  (?<Lanthanum>La)
  (?<Cerium>Ce)
  (?<Praseodymium>Pr)
  (?<Neodymium>Nd)
  (?<Promethium>Pm)
  (?<Samarium>Sm)
  (?<Europium>Eu)
  (?<Gadolinium>Gd)
  (?<Terbium>Tb)
  (?<Dysprosium>Dy)
  (?<Holmium>Ho)
  (?<Erbium>Er)
  (?<Thulium>Tm)
  (?<Ytterbium>Yb)
  (?<Lutetium>Lu)
  (?<Hafnium>Hf)
  (?<Tantalum>Ta)
  (?<Tungsten>W)
  (?<Rhenium>Re)
  (?<Osmium>Os)
  (?<Iridium>Ir)
  (?<Platinum>Pt)
  (?<Gold>Au)
  (?<Mercury>Hg)
  (?<Thallium>Tl)
  (?<Lead>Pb)
  (?<Bismuth>Bi)
  (?<Polonium>Po)
  (?<Astatine>At)
  (?<Radon>Rn)
  (?<Francium>Fr)
  (?<Radium>Ra)
  (?<Actinium>Ac)
  (?<Thorium>Th)
  (?<Protactinium>Pa)
  (?<Uranium>U)
  (?<Neptunium>Np)
  (?<Plutonium>Pu)
  (?<Americium>Am)
  (?<Curium>Cm)
  (?<Berkelium>Bk)
  (?<Californium>Cf)
  (?<Einsteinium>Es)
  (?<Fermium>Fm)
  (?<Mendelevium>Md)
  (?<Nobelium>No)
  (?<Lawrencium>Lr)
  (?<Rutherfordium>Rf)
  (?<Dubnium>Db)
  (?<Seaborgium>Sg)
  (?<Bohrium>Bh)
  (?<Hassium>Hs)
  (?<Meitnerium>Mt)
  (?<Darmstadtium>Ds)
  (?<Roentgenium>Rg)
  (?<Copernicium>Cn)
  (?<Nihonium>Nh)
  (?<Flerovium>Fl)
  (?<Moscovium>Mc)
  (?<Livermorium>Lv)
  (?<Tennessine>Ts)
  (?<Oganesson>Og)
  (?# Regex )
  (?<Element>(?&Actinium)|(?&Silver)|(?&Aluminum)|(?&Americium)|(?&Argon)|(?&Arsenic)|(?&Astatine)|(?&Gold)|(?&Barium)|(?&Beryllium)|(?&Bohrium)|(?&Bismuth)|(?&Berkelium)|(?&Bromine)|(?&Boron)|(?&Calcium)|(?&Cadmium)|(?&Cerium)|(?&Californium)|(?&Chlorine)|(?&Curium)|(?&Copernicium)|(?&Cobalt)|(?&Chromium)|(?&Cesium)|(?&Copper)|(?&Carbon)|(?&Dubnium)|(?&Darmstadtium)|(?&Dysprosium)|(?&Erbium)|(?&Einsteinium)|(?&Europium)|(?&Iron)|(?&Flerovium)|(?&Fermium)|(?&Francium)|(?&Fluorine)|(?&Gallium)|(?&Gadolinium)|(?&Germanium)|(?&Helium)|(?&Hafnium)|(?&Mercury)|(?&Holmium)|(?&Hassium)|(?&Hydrogen)|(?&Indium)|(?&Iridium)|(?&Iodine)|(?&Krypton)|(?&Potassium)|(?&Lanthanum)|(?&Lithium)|(?&Lawrencium)|(?&Lutetium)|(?&Livermorium)|(?&Moscovium)|(?&Mendelevium)|(?&Magnesium)|(?&Manganese)|(?&Molybdenum)|(?&Meitnerium)|(?&Sodium)|(?&Niobium)|(?&Neodymium)|(?&Neon)|(?&Nihonium)|(?&Nickel)|(?&Nobelium)|(?&Neptunium)|(?&Nitrogen)|(?&Oganesson)|(?&Osmium)|(?&Oxygen)|(?&Protactinium)|(?&Lead)|(?&Palladium)|(?&Promethium)|(?&Polonium)|(?&Praseodymium)|(?&Platinum)|(?&Plutonium)|(?&Phosphorus)|(?&Radium)|(?&Rubidium)|(?&Rhenium)|(?&Rutherfordium)|(?&Roentgenium)|(?&Rhodium)|(?&Radon)|(?&Ruthenium)|(?&Antimony)|(?&Scandium)|(?&Selenium)|(?&Seaborgium)|(?&Silicon)|(?&Samarium)|(?&Tin)|(?&Strontium)|(?&Sulfur)|(?&Tantalum)|(?&Terbium)|(?&Technetium)|(?&Tellurium)|(?&Thorium)|(?&Titanium)|(?&Thallium)|(?&Thulium)|(?&Tennessine)|(?&Uranium)|(?&Vanadium)|(?&Tungsten)|(?&Xenon)|(?&Ytterbium)|(?&Yttrium)|(?&Zirconium)|(?&Zinc))
  (?<Num>(?:[1-9]\d*)?)
  (?<ElementGroup>(?:(?&Element)(?&Num))+)
  (?<ElementParenthesesGroup>\((?&ElementGroup)+\)(?&Num))
  (?<ElementSquareBracketGroup>\[(?:(?:(?&ElementParenthesesGroup)(?:(?&ElementGroup)|(?&ElementParenthesesGroup))+)|(?:(?:(?&ElementGroup)|(?&ElementParenthesesGroup))+(?&ElementParenthesesGroup)))\](?&Num))
)
^((?<Brackets>(?&ElementSquareBracketGroup))|(?<Parentheses>(?&ElementParenthesesGroup))|(?<Group>(?&ElementGroup)))+$

解释

  1. (?(DEFINE))部分的第一部分列出了每个周期性元素(按原子序数排序以便于查找)。
  2. Element组在1中列出的每个元素之间充当简单或|。确保每个元素的符号按字母顺序排列第一个字符,然后按符号排序字符长度(以便不捕获,例如,碳C而不是钙Ca
  3. ElementGroup指定一组化学品,格式为:一个或多个Element后跟零个或多个数字,不包括零(由组Num指定)
    • 有效示例
      • C - Element
      • CH - Element后跟另一个Element
      • CH3 - Element后跟另一个ElementNum
      • O2 - Element后跟Num
    • 无效的示例
      • N0 - 0无法明确使用
      • N01 - Num组指定的号码必须以1-9开头或没有号码
      • A - 元素不存在
      • c - 元素不存在 - 区分大小写的正则表达式
  4. ElementParenthesesGroup在括号ElementGroup (之间指定)的一个或多个分组,但至少包含一个ElementGroup
    • 有效示例
      • (CH) - ElementGroup被括号括起来
      • (CH3) - ElementGroup被括号括起来
      • (CH3NO4) - 括号括起来的多个ElementGroup
      • (CH3N04)2 - 多个ElementGroup被括号括起,后跟Num
    • 无效的示例
      • (CH[NO4]) - 只有ElementGroupElementParenthesesGroup
      • 内有效
  5. ElementSquareBracketGroup指定方括号ElementParenthesesGroup ElementGroup之间的[]分组,但至少包含一个ElementParenthesesGroup和另一个组(ElementParenthesesGroupElementGroup
    • 有效示例
      • [CH3(NO4)] - 至少包含一个ElementParenthesesGroup和另一个ElementParenthesesGroupElementGroup
      • [(NO4)CH]2 - 至少包含一个ElementParenthesesGroup和另一个ElementParenthesesGroupElementGroup,后跟Num
      • [(NO4)(CH3)] - 至少包含一个ElementParenthesesGroup和另一个ElementParenthesesGroupElementGroup
    • 无效的示例
      • [(NO4)] - 不包含第二组,括号[ ]是多余的
      • [NO4] - 不包含ElementParenthesesGroup
  6. 其他信息

    我意识到这是一个很长的答案,但OP正在提出一个非常具体的问题,并希望确保满足特定的标准。

    确保设置了以下标志:

    • g - 确保全球匹配
    • x - 确保忽略空格
    • 如果数据跨越多行(由换行符分隔),请使用m表示多行

    注意:Regex只捕获它找到的最后一个类型X(并覆盖以前捕获的所述类型X的组。这是正则表达式的默认行为,没有办法当前覆盖此行为。这可能会给您带来不良后果。您可以使用链接正则表达式中的最后一个示例以及(CH3)2CFCOO(CH2)2Si(CH3)2Cl的示例来查看此内容,因为每个组类型都有多个。

答案 1 :(得分:5)

最好不要手动组装这么大的正则表达式。相反,我们假设我们有一个原子数组@atoms。然后我们可以创建一个匹配任何这些原子的正则表达式,如:

my ($atoms_regex) = map qr/$_/, join '|', map quotemeta, sort @atoms;

(对所有项目进行排序,以便首先使用较短的原子名称,然后使用quotemeta转义所有项目,使用|加入替代项,并编译正则表达式。)

您可以将任何使用过的缩写添加到@atoms数组。

接下来,我们可以编写一个允许分组和编号的正则表达式。我们的正则表达式将匹配任意数量的项目,其中项目可能是原子或组,并且可能后跟一个数字:

my $chemical_formula_regex = qr/
  (?&item)++
  (?(DEFINE)
    (?<item> (?: \((?&item)++\) | \[(?&item)++\] | $atoms_regex ) [0-9]* )
  )
/x;
  • (?(DEFINE) ...)组中,我们可以使用(?<name> ...)定义命名子模式。子模式就像正则表达式的子例程。我们可以使用(?&name)调用这些子模式。这允许我们构造正则表达式而不必重复。

  • /x标志允许我们使用空格和换行符以及注释以更易读的方式布局正则表达式。正则表达不是一个难以理解的混乱!

  • ++量词而非+并非绝对必要,但可以防止不必要的回溯。当匹配失败时,这可能会更快一些。

答案 2 :(得分:0)

由于这篇文章是“正则表达式化学符号”的最高结果,我也想提交一个解决方案。它是一个 Python 脚本,它使用正则表达式来匹配 A#B# 类型的化学公式,其中 A 和 B 是化学符号,# 是数字。脚本作为实现匹配,然后用 \ce{} 包围匹配以在 LaTeX 中使用。如果捕获位于用户定义的列表中,它还包括排除匹配项的功能,这意味着不会匹配诸如“I”和“In”之类的词。 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='')