Java:解析字符串并将其分解为“术语”

时间:2016-04-04 16:14:22

标签: java string parsing

我在围绕化学配方的项目上遇到了麻烦。我有两个课程,Term和Formula。

术语接收诸如“H”或“C2”之类的输入 - 只有一个字母和任意数量的后续数字。它的'字段是元素和原子 - 元素存储元素的字符,'H'或'C',原子存储原子数的int值,1或2。

我是这样写的构造函数;

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)}等。

我真的在努力弄清楚如何让构造函数识别输入String中的术语 - 我已经考虑过valueOf和indexOf,或者可能是substring,但似乎无法得到它。我的第一次尝试是;

 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循环。我为帖子的长度道歉,但任何帮助都会非常感激。

4 个答案:

答案 0 :(得分:3)

这看起来像是使用java.util.Patternjava.util.Matcher进行正则表达式解析的工作。特别是,您希望让正则表达式指定组,以便您可以通过Term上的group()方法将每个Matcher作为一个组进行访问。

这是解析输入分子并实例化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内进行的解析,还支持在其周期符号中包含两个字符的原子。

正则表达式&#34;组&#34;是一个正则表达式匹配的一部分,然后您可以通过索引引用。因此,正则表达式不仅可用于测试完整输入表达式是否匹配,还可用于解析匹配的输入表达式并将其分解为与正则表达式本身的片段匹配的片段 - 即组。您使用括号在正则表达式中定义组 - 请务必查看我在本文底部提到的所有教程链接,以便您完全理解。

在上面的代码中,正则表达式Matcher遍历您的输入。它与find()遇到的每个块都匹配正则表达式中指定的组的集合。群组0始终是整个匹配的群组,群组1是第一群组,群组2是第二群组。由于第二组(原子)是可选的,我们测试它是否为空。还请注意,Pattern编译应该只发生一次,因此它的实例化为static final常量。

这里的主要内容是我们已经从Term中解除了所有解析并将所有内容集中在这一个解析例程中。为了支持这一点,我为你重写了Term构造函数。

理解正则表达式对于任何类型的解析都是必不可少的。我建议the Oracle tutorial进行基于Java的简介。特别是,请确保您理解the section on groups,因为这是我们利用上述方法来分解您的分子。

正如@AJNeufeld在评论中指出的那样,我的解决方案不会支持更复杂的分子,如Al2(SO4)3。了解此代码和引用的教程后,您应该能够对其进行修改以支持此类输入。

答案 1 :(得分:0)

You are correct in assuming you need a for loop. Your for loop is actually very close to correct. The only problem is that you're only add if the current character is a letter and the next character is also a letter. If you change it as follows:

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)));
      }
  } 
}

Hope this helps!

答案 2 :(得分:0)

Oldschool but reliant:

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);
    }
}

答案 3 :(得分:0)

Here is an example that creates an ArrayList by parsing terms. It could also handle terms with a second lower case character if you update the Term constructor.

    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;
}