2016-04-04 18 views
2

化学式の周りを回転するプロジェクトに問題があります。私はTermとFormulaという2つのクラスを持っています。Java:文字列を解析して「用語」に分解する

用語は、「H」または「C2」などの入力を受け取ります。単一の文字と任意の数の次の数字のみを使用します。要素は「H」または「C」の要素の文字を格納し、アトムは1または2の原子数のint値を格納します。

コンストラクタはそのように記述しました。

public Term(String s) 
    { 

     if(s.length() == 1){ 
      element = s.charAt(0); 
      atoms = 1; } 

     else { 
      element = s.charAt(0); 
      String sA = s.substring(1,s.length()); 
      atoms = Integer.parseInt(sA); 
     } 
    } 

私はそれを理解しています。私の問題は、 "CH3CH2CH2CH2CH2CH3"のような文字列を受け取るクラスの数式です。用語と呼ばれるTermオブジェクトを格納するArrayListが必要です。私は "CH2O"のような公式を受け取ることができるコンストラクタを書く必要があり、{Term( 'C'、1)、Term( 'H'、2)、Term( 'O'、1) }等

私は実際には、入力文字列内の用語を識別する方法を考え出すのに苦労しています - 私はvalueOfとindexOfについて考えましたが、部分文字列かもしれませんが、それを得ることはできません。私の最初の試みは、

terms = new ArrayList<>(); 
    for(int i = 0; i <= s.length();i++) 
    { 
     if ((Character.isUpperCase(s.charAt(i))) && (Character.isUpperCase(s.charAt(i+1))) 
     { Term formulaS = new Term(s.charAt(i)); 
      terms.add(formulaS); } 

私はその非常に間違って理解し、唯一の唯一の文字ではなく、そのような「H」としての原子の数を持っている用語を特定します。答えにforループが含まれていると感じています。私は投稿の長さをお詫び申し上げますが、どんな助けでも大歓迎です。

+2

[レキシカル文法](https://en.m.wikipedia.org/wiki/Lexical_grammar)で、通常は正規表現で定義されます。 –

+1

おそらく入力文字列全体をただちに大文字にすることができます。その後、大文字のチェックを行う必要はありません。また、forループでいくつかのことを行う必要があります。まず、あなたはあまりにも遠くに行きます。私は ManoDestra

+0

'' Na ''や' 'Cl" 'のような2文字の原子名を扱う必要がありますか?それとも、あなたは1つの文字記号だけの原子で構成された分子が与えられることが保証されていますか? – AJNeufeld

答えて

3

これは、java.util.Patternjava.util.Matcherを使用する正規表現解析の仕事のようです。特に、正規表現でグループを指定したい場合は、group()メソッド(Matcher)でグループとしてTermにアクセスできます。

入力分子を解析してTermオブジェクトをインスタンス化するコードです。私は@AJNeufeldからの非常に有益な示唆を元に、それを元に戻しました。

public class MoleculeParser { 
    private static final Pattern PATTERN = Pattern.compile ("([A-Z][a-z]?)(\\d*)"); 

    public static List<Term> parseMolecule (String molecule) { 
     List<Term> terms = new ArrayList<>(); 

     Matcher matcher = PATTERN.matcher (molecule); 
     while (matcher.find()) { 
      String element = matcher.group(1); 
      String group2 = matcher.group(2); 
      if (!group2.isEmpty()) { 
       int atoms = Integer.parseInt (matcher.group(2)); 
       terms.add(new Term(element, atoms)); 
      } 
      else { 
       terms.add(new Term(element)); 
      } 
     } 

     return terms; 
    } 

    public static void main (String[] args) { 
     String str = "CH3CH2CH2CH2CH2CH3"; 
     System.out.println (parseMolecule (str)); 

     str = "C12H22O11 "; 
     System.out.println (parseMolecule (str)); 

     str = "SiCl4"; 
     System.out.println (parseMolecule (str)); 
    }  
} 


public class Term { 
    public Term (String element) { 
     this(element, 1); 
    } 

    public Term (String element, int atoms) { 
     _element = element; 
     _atoms = atoms; 
    } 

    @Override 
    public String toString() { 
     return _element + (_atoms != 1 ? _atoms : "") ; 
    } 

    private String _element; 
    private int _atoms; 
} 

このコードは、あなたの当面の問題を解決するだけでなく、あなたがTermの内部でやっていた構文解析を処理するだけではなく、正規表現グループの力を活用し、また彼らの定期的なシンボルで2つの文字を持つ原子をサポートしています。

正規表現「グループ」は正規表現の一部です。この正規表現は、索引で参照できます。したがって、正規表現は完全な入力式が一致するかどうかをテストするだけでなく、一致した入力式を解析し、正規表現そのもの(つまりグループ)に一致する部分に分割するためにも使用できます。あなたは正規表現の中でグループを定義するために括弧を使います。この記事の一番下にあるすべてのチュートリアルのリンクをよく読んで、これを完全に理解してください。

上記のコードでは、正規表現Matcherが入力を繰り返します。 find()で遭遇する各チャンクは、正規表現で指定されたグループのセットと一致します。グループ0は、常に一致するグループのグループであり、グループ1が最初のグループであり、グループ2が2番目のグループです。 2番目のグループ(アトム)はオプションなので、空であるかどうかをテストします。また、Patternのコンパイルは1回だけ実行する必要があるため、static finalという定数でインスタンス化してください。

ここでは、すべての解析をTermから取り除き、この1つの解析ルーチンにすべてを集中させています。これをサポートするために、Termコンストラクタを書き直しました。

あらゆる種類の解析には、正規表現を理解することが不可欠です。良いJavaベースの導入についてはthe Oracle tutorialをお勧めします。特に、the section on groupsを理解していることを確認してください。なぜなら、これが分子を破壊するために上に取り入れているからです。

@AJNeufeldがコメントで指摘したように、私の解決策はAl2(SO4)3のようなより複雑な分子をサポートしません。このコードと引用されたチュートリアルを理解すれば、そのような入力をサポートするように修正することができます。

+1

私は '\\ d *'を使っています...あなたは "砂糖" ... C12H22O11 ...各原子の後に複数の数字を得ることができます。 – AJNeufeld

+0

合意 - ありがとう。これに取り組んで、一文字よりも長い名前(ほとんどのもの)を持つすべての要素をサポートしています。 –

+1

優秀! 'group(1)'が全体の用語で、 'group(2)'が原子シンボルであるように '(([AZ] [az]?)(\\ d *)) group(3) 'はカウント(または" 1 "を意味する空の文字列)です。 – AJNeufeld

0

forループが必要であると仮定して正しいですか?あなたのforループは、実際には非常に近いです。唯一の問題は、現在の文字がの場合はifを追加することです。次の文字も文字です。次のように変更すると:

for (int i = 0; i < s.length();i++) { 
    if ((Character.isUpperCase(s.charAt(i)))) { // If current character is a letter 
    if (Character.isUpperCase(s.charAt(i+1))) { 
     // If the next char is also letter, add current only 
     terms.add(new Term(s.substring(i,i+1))); 
     } else { 
     // If next char is number, add both 
     terms.add(new Term(s.substring(i,i+2))); 
     } 
    } 
} 

0

オールドスクールなく、自立:ここ

static void lex(String s) { 
    String token = ""; 

    for (int i = 0; i < s.length(); i++) { 
     char c = s.charAt(i); 
     if (token.length() > 0 && !Character.isDigit(c)) { 
      System.out.println(token); 
      token = ""; 
     } 
     token += c; 
    } 

    if (!token.isEmpty()) { 
     System.out.println(token); 
    } 
} 
0

は、用語を解析することにより、ArrayListのを作成する例です。また、Termコンストラクタを更新すると、2番目の小文字でタームを処理することもできます。あなたがベースの意味の「トークン」に文字列を解体する過程である、[字句解析](https://en.m.wikipedia.org/wiki/Lexical_analysis)を検討する必要がある

public ArrayList<Term> initList(String initString) { 
    ArrayList<Term> terms = new ArrayList<Term>(); 
    int nextTerm = 0; 
    for(int i = 1; i < initString.length();i++) 
    { 
     while(i < initString.length() && 
       !Character.isUpperCase(initString.charAt(i))) { 
      i++; 
     } 
     // parse the next term from the nextTerm start index to the current i index 
     terms.add(new Term(initString.substring(nextTerm, i))); 
     nextTerm = i; 
    } 
    return terms; 
} 
関連する問題