一个严格的正则表达式,用于匹配化学式。

7
在使用Perl处理大型文本化学数据库时,我遇到了使用正则表达式匹配化学式的问题。我已经看过这两个以前的话题(这个这个),但那里提供的答案对我的要求来说太宽松了。
具体来说,我的(虽然有限的)研究导致我找到了这篇文章,其中给出了一个用于当前接受的化学符号的正则表达式,我将在此处引用它以供参考
[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。)
与以前的问题一样,我还需要匹配数字、完整的括号和完整的方括号,以便匹配C2H6O(CH3)2CFCOO(CH2)2Si(CH3)2Cl
那么,如何将先前的解决方案与用于匹配有效化学元素的大正则表达式结合起来,以严格匹配化学式?
(如果添加并不麻烦,非常感谢提供每个部分如何人工解析正则表达式的详细说明,但这并非必需。)

2
说实话,仅凭正则表达式可能无法覆盖验证所有化学表达式。原因在于,化学式可能具有任意深度的嵌套括号。因此,在这里使用解析器可能更合适。 - Tim Biegeleisen
1
这似乎有点疯狂。化学家在编写这种公式时不遵循严格的语法规则。有机化学家经常会在其中加入一个“R”来表示任意的碳基团,或者一个“Ph”来表示苯基团,或者一个“X”来表示卤素等等。这只是潜在的不规则性的一小部分。你确定你必须这样做吗? - Chris
@Chris 此外,仅仅因为一个公式通过了正则表达式并不意味着该分子实际存在。这里有许多层次的验证。 - Tim Biegeleisen
大概来说,原帖中使用的文本应该负责了解它的化学性质,目标只是识别文本何时表达一个公式。但是... - Chris
2
@TimBiegeleisen Perl和PCRE正则表达式引擎能够处理递归语法。严谨地说,它们至少可以识别所有LL(*)(LL-regular)语言。 - amon
显示剩余5条评论
3个回答

9

简述

我决定创建一个大型正则表达式来实现您想要的功能(但仍然保持干净的正则表达式)。这个正则表达式将与循环一起使用,以处理括号或圆括号组的匹配。


假设

由于OP没有给出完整的正面和负面匹配列表,因此我假设以下内容:

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

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


答案

在此处查看正在使用的正则表达式

代码

(?(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后面跟着另一个Element和一个Num
      • 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]) - 在ElementParenthesesGroup内只有ElementGroup是有效的
  5. ElementSquareBracketGroup 指定了ElementParenthesesGroupElementGroup的分组,位于方括号[ ]之间,但至少包含一个ElementParenthesesGroup和另一个组(ElementParenthesesGroupElementGroup
    • 有效示例
      • [CH3(NO4)] - 包含至少一个ElementParenthesesGroup和另一个ElementParenthesesGroupElementGroup
      • [(NO4)CH]2 - 包含至少一个ElementParenthesesGroup和另一个

        额外信息

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

        确保设置以下标志:

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

        注意:正则表达式仅捕获它找到的最后一个X类型的组,并覆盖先前捕获的同类X组。 这是正则表达式的默认行为,目前没有办法覆盖此行为。这可能会给您带来不良结果。 您可以在链接的正则表达式中的最后一个示例以及您的示例(CH3)2CFCOO(CH2)2Si(CH3)2Cl中看到这一点,因为每种组类型都有多个。


不错的布局。我已经用现有的名称替代了临时名称。尽管如此,明天上班我会试一试! - Krissy Budd
@KrissyBudd 谢谢!您是否也想保留旧的元素符号?我可以在新的元素名称下添加UubUut等。例如,这将显示为(?<Copernicium>Cn|Uub) - ctwheels
不,我认为最好只使用当前的名称。 (在这方面,我们已经进行了搜索和替换。) - Krissy Budd
@KrissyBudd 太好了!我会在regex101上更新链接,与此处的正则表达式信息相同。 - ctwheels

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标志允许我们使用空格、换行和注释来更加可读地布置正则表达式。正则表达式不必是难以理解的混乱!

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


0

由于这篇文章是“正则表达式化学符号”搜索结果的顶部,我也想提交一个解决方案。它是一个使用正则表达式匹配A#B#类型化学公式的Python脚本,其中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='')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接