查找文件中的所有化学符号

时间:2012-03-11 05:49:57

标签: bash sed

我的文件中包含许多化学式。我需要标记任何化学式的文本。我想在文件中搜索包含至少一个化学符号和至少一个数字的组合的任何地方,并在其周围添加\chemical{}。例如。 H2O变为\chemical{H2O}FeS2变为\chemical{FeS2}

  • 化学品以空格()或正斜线(/"为界。例如:/Ar变为/\chemical{Ar},但Arizona不应该被确定为“\ chemical {Ar} izona”。
  • 应忽略不包含数字的组合。
  • 我发现这个列表我认为有各种可能的化学名称:“Ac,Ag,Al,Am,Ar,As,At,Au,B,Ba,Be,Bh,Bi,Bk,Br,C,Ca ,Cd,Ce,Cf,Cl,Cm,Cn,Co,Cr,Cs,Cu,Db,Ds,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,Uuh,Uuo,Uup,Uuq,Uus,Uut,V,W,Xe,Y,Yb,Zn ,Zr“。

如何找到文件中出现的所有化学公式?

2 个答案:

答案 0 :(得分:6)

我使用Perl。它比单调更令人兴奋。你创建一个包含所有替代符号的正则表达式,然后从那个和其他一些零碎的部分构建一个更复杂的正则表达式:

#!/usr/bin/env perl
use strict;
use warnings;

my $symbols = "Ac|Ag|Al|Am|Ar|As|At|Au|B|Ba|Be|Bh|Bi|Bk|Br|C|Ca|Cd|Ce|Cf|Cl|Cm|Cn|Co|Cr|Cs|Cu|Db|Ds|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|Uuh|Uuo|Uup|Uuq|Uus|Uut|V|W|Xe|Y|Yb|Zn|Zr";

#my $symbols = "Ac|Ag|Al|...|Y|Yb|Zn|Zr";

my $regex = qr{ ([/ ]) ( (?:$symbols) (?: \d (?:$symbols) )* \d? ) ([ /]) }x;

printf "$regex\n";

while (<>)
{
    s/$regex/$1\\chemical{$2}$3/g;  # Handles first and third (, ...) in H2O CO2 H2SO4
    s/$regex/$1\\chemical{$2}$3/g;  # Handles second (fourth, ...)
    print $_;
}

第一次捕获处理符号前的空格或斜线。第二次捕获是令人毛骨悚然的,使用$symbols中的幽默字符串两次。 (?:...)纯粹用于分组,而不是捕获。该模式寻找化学符号,可选地后跟零个或多个数字序列和另一个符号,可能具有尾随数字。请注意,这是您指定的,但会遗漏化合物,例如H 2 SO 4 ,CO 2 ,KMnO 4 ,依此类推。你可以通过简单的改编选择那些:

my $regex = qr{ ([/ ]) ( (?:$symbols) (?: \d* (?:$symbols) )* \d* ) ([ /]) }x;

我也假设所有化合物都有一位数。这适用于许多人,但是一些较长的碳氢化合物不会那么好:CH 4 ,C 2 H 6 ,C 3 H 8 ,C 4 H 10 ,...再次,您可以通过替换0-来处理or-1 ?,0或更多*。在列表中的化合物,行开头的化合物,行尾的化合物等之后,您仍然遇到逗号问题 - 您的规范将它们全部排除在外。

您最好用\b替换第一次和第三次捕获,以标记“单词”和“非单词”之间的边界,其中化学符号将被视为单词。这涉及逗号以及开头和结尾的问题,但是比你指定的要多。

my $regex = qr{ \b ( (?:$symbols) (?: \d* (?:$symbols) )* \d* ) \b }x;

printf "$regex\n";

while (<>)
{
    s/$regex/\\chemical{$1}/g;
    print $_;
}

请注意,此配方不需要双重替代;一个人就足够了,所以它绝对更清洁。

答案 1 :(得分:2)

使用awk

awk 'BEGIN{
  strElements="Ac Ag Al Am Ar As At Au B Ba Be Bh Bi Bk Br C Ca Cd Ce Cf Cl Cm Cn Co Cr Cs Cu Db Ds 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 Uuh Uuo Uup Uuq Uus Uut V W Xe Y Yb Zn Zr"
  n = split(strElements, arrElements)
  for(i = 0; i < n; i++)
    hashElements[arrElements[i]] = 1}
  {for(i = 1; i <= NF; i++) {
    str = substr($i, 1, 1) == "/" ? substr($i, 2) : $i
    n = split(str, elements, "[0123456789]+")
    while (n > 0) {if (!(elements[n] in hashElements)) break; n--}
    if (n == 0)
      $i = (substr($i, 1, 1) == "/" ? "/" : "") "\\chemical{" str "}"
    }
  print}' your_file

脚本的想法如下:

  1. 构建所有元素的哈希值(在awk中,数组是关联的)。
  2. 对于每一行,一次取一个单词,按编号拆分,看看每个子单词是否为元素。
  3. 如果是这种情况,请用所需的字符串包围化学品。
  4. 当然,需要添加一些逻辑来考虑特殊字符/