用于解析化学式的正则表达式

10
我需要一种将化学式分解成组成部分的方法。结果应该像这样:
   Ag3PO4 -> [Ag3, P, O4]
      H2O -> [H2, O]
   CH3OOH -> [C, H3, O, O, H]
Ca3(PO4)2 -> [Ca3, (PO4)2]

我不懂正则表达式的语法,但我知道我需要像这样的东西

[一个可选的括号][一个大写字母][0或多个小写字母][0或多个数字][一个可选的括号][0或多个数字]

这个可以工作

NSRegularExpression *regex = [NSRegularExpression
                              regularExpressionWithPattern:@"[A-Z][a-z]*\\d*|\\([^)]+\\)\\d*"
                              options:0
                              error:nil];
NSArray *tests = [[NSArray alloc ] initWithObjects:@"Ca3(PO4)2", @"HCl", @"CaCO3", @"ZnCl2", @"C7H6O2", @"BaSO4", nil];
for (NSString *testString in tests)
{
    NSLog(@"Testing: %@", testString);
    NSArray *myArray = [regex matchesInString:testString options:0 range:NSMakeRange(0, [testString length])] ;
    NSMutableArray *matches = [NSMutableArray arrayWithCapacity:[myArray count]];

    for (NSTextCheckingResult *match in myArray) {
        NSRange matchRange = [match rangeAtIndex:0];
        [matches addObject:[testString substringWithRange:matchRange]];
        NSLog(@"%@", [matches lastObject]);
    }
}

1
我不确定化学式是否属于一种常规语言。你或许可以试着破解它,但很可能不会完美无缺。 - user764357
@LegoStormtroopr 请看一下我上面的尝试,只要我能让类似的东西工作,那我就满意了。 - michaelsnowden
5个回答

23

(PO4)2真的是与众不同的存在。

让我们从简单的开始,先将没有括号的项目进行匹配:

[A-Z][a-z]?\d*

使用上述正则表达式,我们可以成功解析Ag3PO4H2OCH3OOH

接下来我们需要为组添加表达式。单独的组可以使用以下语法进行匹配:

\(.*?\)\d+

所以我们添加 or 条件:

[A-Z][a-z]?\d*|\(.*?\)\d+

正则表达式可视化

演示

这个正则表达式适用于给定的情况。但是如果您有更多样本,可能会遇到一些问题。

注意:它在处理嵌套括号时会出现问题,例如 Co3(Fe(CN)6)2

如果您想处理这种情况,可以使用以下正则表达式:

[A-Z][a-z]?\d*|(?<!\([^)]*)\(.*\)\d+(?![^(]*\))

正则表达式可视化

对于Objective-C,您可以使用不带顾虑断言的表达式:

[A-Z][a-z]?\d*|\([^()]*(?:\(.*\))?[^()]*\)\d+

正则表达式可视化

演示

或者带有重复的正则表达式(我不知道这样的公式,但如果存在像A(B(CD)3E(FG)4)5这样多个括号块在一个内部的情况)。

[A-Z][a-z]?\d*|\((?:[^()]*(?:\(.*\))?[^()]*)+\)\d+

正则表达式可视化

演示


非常感谢你,也感谢其他所有帖子的作者们提供的快速帮助。这真的帮助我理解了正则表达式,并且它们都能完成工作! - michaelsnowden
@doctordoder,它缺少了2,应该是(Fe(CN)6) 2。我添加了正则表达式来处理这种情况。 - Ulugbek Umirov
@doctordoder 我的错,Objective-C不支持后顾*量词。 - Ulugbek Umirov
@UlugbekUmirov:我并不是在批评,相反地,正则表达式应该适合其数据,而且没有递归的情况下无法完美匹配嵌套模式。贪婪性会将(Ag(PO)2)2H2O(Ag(NO2)2)2视为一个匹配项,而不是三个。 - Robin
2
离题了,但是你是如何生成那些花哨的正则表达式模式可视化图的? - user764357
显示剩余6条评论

4
当你遇到一个括号组时,你不想解析里面的内容,对吗?
如果没有嵌套的括号组,你可以简单地使用
[A-Z][a-z]*\d*|\([^)]+\)\d*

\d 是代表数字 [0-9] 的缩写,[^)] 表示除了括号之外的任何字符。

在这里查看演示


3

这应该就可以工作了:

/(\(?)([A-Z])([a-z]*)([0-9]*)(\))?([0-9]*)/g

为什么需要这么多捕获组? - Robin
我想象中 OP 希望每个部分都作为单独的匹配。 - Christof

2

这个模式取决于你的正则表达式引擎,应该可以工作。使用gm选项:
([A-Z][a-z]*\d*)|(\((?:[^()]+|(?R))*\)\d*)
演示


0

最好将字符集限制在合法的化学名上。简单形式如下:

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

这不处理括号组。

这是我们在圣地亚哥Python用户组会议期间解决的问题。


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