「CH3COOH」のような化学式を取り、その記号でいっぱいのある種のコレクションを返すアプリのメソッドを作成しようとしています。
CH3COOH は [C,H,H,H,C,O,O,H] を返します。
私はすでにちょっと動いているものを持っていますが、それは非常に複雑で、多くのネストされた if-else 構造とループを含む多くのコードを使用しています。
String.split で何らかの正規表現を使用するか、他の素晴らしい単純なコードでこれを行う方法はありますか?
C6H2(NO2)3CH3 のようなより複雑な式を含む、分子式の解析方法に関する一連の記事をいくつか作成しました。
最新のものは、PyCon2010 でのプレゼンテーション「PLY and PyParsing」で、サンプル問題として分子式評価器を使用して、これら 2 つの Python 解析システムを比較しています。私のプレゼンテーションのビデオもあります。
このプレゼンテーションは、私が ANTLR を使用して分子式パーサーを開発した3 部構成の記事に基づいていました。パート 3では、ANTLR ソリューションを手書きの正規表現パーサーと比較し、PLY と PyParsing のソリューションを比較します。
正規表現と PLY のソリューションは、Python でパーサーを記述する 2 つの方法に関する 2部構成のシリーズで最初に開発されました。
regexp ソリューションとベース ANTLR/PLY/PyParsing ソリューションでは、[AZ][az]?\d* のような正規表現を使用して数式内の用語を照合します。これは@David Mが提案したものです。
これがPythonで解決されたものです
import re
# element_name is: capital letter followed by optional lower-case
# count is: empty string (so the count is 1), or a set of digits
element_pat = re.compile("([A-Z][a-z]?)(\d*)")
all_elements = []
for (element_name, count) in element_pat.findall("CH3COOH"):
if count == "":
count = 1
else:
count = int(count)
all_elements.extend([element_name] * count)
print all_elements
これを実行すると (酢酸、CH3COOH を使用するようにハードコードされています)
['C', 'H', 'H', 'H', 'C', 'O', 'O', 'H']
この短いコードは、分子式が正しいことを前提としていることに注意してください。"##$%^O2##$#" のようなものを指定すると、知らないフィールドは無視され、['O', 'O'] が指定されます。それが望ましくない場合は、もう少し堅牢にする必要があります。
C6H2(NO2)3CH3 などのより複雑な式をサポートする場合は、ツリー データ構造、具体的には (@Roman が指摘しているように) 抽象構文ツリー (ほとんどの場合 AST と呼ばれます) について少し知る必要があります。これは複雑すぎてここに入ることができないので、詳細については私の講演とエッセイを参照してください。
正しく大文字になっていると仮定すると、方程式の各記号は次の正規表現と一致します。
[A-Z][a-z]*\d*
(化学的に困難な人のために、元素の記号は常に大文字で、オプションで小文字の 1 つまたは 2 つが続きます。たとえば、水銀は Hg です)。
次のように、要素記号と数字をグループでキャプチャできます。
([A-Z][a-z]*)(\d*)
そうです、理論的には、これは正規表現が役立つものです。C 6 H 2 (NO 2 ) 3 (CH 3 ) 3のような式を扱っている場合、もちろん、仕事は少し難しくなります...
単純なケースのみを処理する必要がある場合は、正規表現を使用したソリューションが最適な方法です。それ以外の場合は、Abstract Syntax Treeのようなものを作成して評価するか、Polish Notationを使用する必要があります。
たとえば、TNT 式C6H2(NO2)3CH3は次のように表示する必要があります。
(+ (* C 6) (* H 2) (* (+ N (* O 2)) 3) C (+ H 3))
化学式を化学マークアップ言語で表現することを検討しましたか?それは非常に用途が広く、これらの化学的フォーラムまたは化合物を2Dから3Dでレンダリングできるツール/ビューアーがたくさんあります。
私は化学式のモル質量計算を必要とするプログラムに取り組んでいるので、さまざまな式で機能するソリューションを作成しました。
たとえば、"(CH3)16(Tc(H2O)3CO(BrFe3(ReCl)3(SO4)2)2)2MnO4" は " 16C 48H 2Tc 12H 6O 2C 2O 4Br 12Fe 12Re 12Cl 8S 32O Mn 4O" (この化合物は作られていますが、ちょっと、うまくいきます!)
このコードは C# で書かれているため、投稿していません。興味があれば、投稿できます。Javaタグに気付く前に、実際に完全な回答を書きました。
とにかく、基本的には、括弧で一致するアトムのブロックを再帰的にグループ化することで機能します。2Pb (ただし、(Pb)2 または Pb2 は機能します) や OH- などの荷電化合物などの係数は処理しません。
シンプルでもエレガントでもありません。私は実用的な解決策が欲しかったので、もっと良い方法があることを知っています(正規表現を試したことさえありません!)。しかし、それは私が必要とする数式で機能します。おそらく、あなたの数式にも適しています。
ここに私が実行したいくつかのテストケースがあります。それらを見て、C# コードがまだ役に立つかどうか教えてください。形式は(入力、期待される出力)です
("Pb ", " Pb");
("H ", " H");
("Pb2 ", " 2Pb");
("H2 ", " 2H");
("3Pb2 ", " 6Pb");
("Pb2SO4", " 2Pb S 4O");
("PbH2 ", " Pb 2H");
("(PbH2)2 ", " 2Pb 4H");
("(CCC)2 ", " 2C 2C 2C");
("Pb(H2)2 ", " Pb 4H");
("(Pb(H2)2)2 ", " 2Pb 8H");
("(Pb(H2)2)2NO3 ", " 2Pb 8H N 3O");
("(Ag(Pb(H2)2)2)2SO4 ", " 2Ag 4Pb 16H S 4O");
("Pb(CH3(CH2)2CH3)2", " Pb 2C 6H 4C 8H 2C 6H");
("Na2(CH3(CH2)2CH3)2", " 2Na 2C 6H 4C 8H 2C 6H");
("Tc(H2O)3Fe3(SO4)2", " Tc 6H 3O 3Fe 2S 8O");
("Tc(H2O)3(Fe3(SO4)2)2", " Tc 6H 3O 6Fe 4S 16O");
("(Tc(H2O)3(Fe3(SO4)2)2)2", " 2Tc 12H 6O 12Fe 8S 32O");
("(Tc(H2O)3CO(Fe3(SO4)2)2)2", " 2Tc 12H 6O 2C 2O 12Fe 8S 32O");
("(Tc(H2O)3CO(BrFe3(ReCl)3(SO4)2)2)2MnO4", " 2Tc 12H 6O 2C 2O 4Br 12Fe 12Re 12Cl 8S 32O Mn 4O");
("(CH3)16(Tc(H2O)3CO(BrFe3(ReCl)3(SO4)2)2)2MnO4", " 16C 48H 2Tc 12H 6O 2C 2O 4Br 12Fe 12Re 12Cl 8S 32O Mn 4O");