正则表达式用于解析Java中的化学方程式

时间:2016-12-14 16:32:58

标签: java regex

我有一个应该从控制台读取一些字符串的程序。如果出现String“end”,它应该开始计算并在控制台中写一个String。

我正在阅读的字符串是一个化学方程式。等式用以下两个字符分开:->。我应该证明两边的原子数量是否相同。我发现了this帖子并尝试实现它,但我对正则表达式有疑问。

例如:

如果在公式之前有一个数字,我的正则表达式可以读取并计算化学方程式:

2 HCl + 2 Na -> 2 NaCl + H2

但是如果没有数字,那么它就不能正确计算:

HCl + Na -> NaCl + H2

我的代码:

public static void main(String[] args) {
    Scanner s = new Scanner(System.in);
    List<String> list = new ArrayList<String>();
    String input = "";
    while (!(input.equals("end"))) {
        input = s.nextLine();
        list.add(input);
    }
    int before = 0;
    int after = 0;
    list.remove(list.size() - 1);

    for (int i = 0; i < list.size(); i++) {
        String string = list.get(i);
        string = string.replace("-", "");
        String[] splitted = string.split(">");
        Pattern firstPattern = Pattern.compile("(\\d+) (\\w+)");
        Matcher firstMatcher = firstPattern.matcher(splitted[0]);
        while (firstMatcher.find()) {
            int element = Integer.parseInt(firstMatcher.group(1));
            String count = firstMatcher.group(2);
            final Pattern pattern = Pattern.compile("\\d+"); // the regex
            final Matcher matcher = pattern.matcher(count); // your string
            final ArrayList<Integer> ints = new ArrayList<Integer>(); // results
            while (matcher.find()) { // for each match
                ints.add(Integer.parseInt(matcher.group())); // convert to
                                                                // int
            }
            for (int j = 0; j < ints.size(); j++) {
                before = before + element * ints.get(j);
            }

        }
        Pattern secondPattern = Pattern.compile("(\\d+) (\\w+)");
        Matcher secondMatcher = secondPattern.matcher(splitted[1]);
        while (secondMatcher.find()) {
            int element = Integer.parseInt(secondMatcher.group(1));
            String count = secondMatcher.group(2);
            final Pattern pattern = Pattern.compile("\\d+"); // the regex
            final Matcher matcher = pattern.matcher(count); // your string
            final ArrayList<Integer> ints = new ArrayList<Integer>(); // results
            while (matcher.find()) { // for each match
                ints.add(Integer.parseInt(matcher.group())); // convert to
                                                                // int
            }
            for (int j = 0; j < ints.size(); j++) {
                after = after + element * ints.get(j);
            }
        }
        if (before == after) {
            System.out.println("formally correct");
        } else {
            System.out.println("incorrect");
        }
    }
}

以下是一些用于尝试的化学方程式示例:

输入:

+ Na - > NaCl + H2

2 HCl + 2 Na - > 2 NaCl + H2

<12> CO2 + 6H2O - > 2 C6H12O6 + 12O2

输出:

不正确

正式更正

不正确

2 个答案:

答案 0 :(得分:2)

我不确定这是否是你需要的。但是为了获得等式的各个部分,可以使用以下regex

\w+ // matches any word character (equal to [a-zA-Z0-9_])

请点击链接了解详情。要获得字符串的左右部分,我们可以使用"HCl + Na -> NaCl + H2".split("->")将其拆分。之后我们可以进行计算:

final Pattern pattern = Pattern.compile("\\w+");
Arrays.asList(
            "HCl + Na -> NaCl + H2",

            "2 HCl + 2 Na -> 2 NaCl + H2",

            "12 CO2 + 6 H2O -> 2 C6H12O6 + 12 O2"
).stream().flatMap(s -> Stream.of(s.split("->"))
).peek(s -> System.out.println("part of equation: " + s))
.forEach(s -> 
        {
          Matcher match = pattern.matcher(s);
          while (match.find()) {
            System.out.println(match.group());
          }
        }
);

我希望这会有所帮助。

答案 1 :(得分:2)

所以,这是我在你的逻辑中可以找到的问题:

  1. 您正在使用Pattern.compile("(\\d+) (\\w+)")来匹配任何一方的每个组件。在模式中,您尝试匹配1 or more digits followed by a space followed by 1 or more word characters。但在那里,数字是可选的。因此,您需要它\\d*而不是第一个捕获组内容。空间也是可选的。所以你需要在模式中指定相同的。并且为了避免数字与第二个捕获组匹配(因为第一个组变为可选),您需要使用([A-Z]\\w*)。这确保了数字(如果有)将与第一组本身匹配。因此,用于匹配任何一方的每个组件的模式应为Pattern.compile("(\\d*) ?([A-Z]\\w*)")
  2. 您正在使用Pattern.compile("\\d+")来匹配原子数(在H2中为2)。如果任何元素都有单个原子,你将错过计算单个原子,即如果你有CaCl2,你必须把它算作1个原子的Ca和2个Cl的原子。为此,您需要单独匹配每个元素,这可以使用Pattern.compile("[A-Z][a-z]*(\\d*)")之类的模式完成。

  3. 您没有以正确的方式计算总数。将每个元素的分子和原子计数默认为1并将它们乘以每个元素,并将所有产品相加以得到总数。

  4. 还有2条建议:

    1. 由于您在每一侧都有相同的计算逻辑,因此请定义一个函数并调用它两次。
    2. ->分隔。我认为您不需要先删除连字符,然后按>分割。
    3. 在进入代码之前尝试自己修改逻辑

      这是我定义用于计算一方总计的函数的方式:

      private static int calculateCount(String eqPart) {
          Matcher matcher = Pattern.compile("(\\d*) ([A-Z]\\w*)").matcher(eqPart);
          int totalCount = 0;
          while (matcher.find()) {
              String moleculeCountStr = matcher.group(1);
              int moleculeCount = moleculeCountStr.isEmpty() ? 1 : Integer.parseInt(moleculeCountStr);
              String molecule = matcher.group(2);
              Matcher moleculeMatcher = Pattern.compile("[A-Z][a-z]*(\\d*)").matcher(molecule);
              while (moleculeMatcher.find()) {
                  String atomCountStr = moleculeMatcher.group(1);
                  int atomCount = atomCountStr.isEmpty() ? 1 : Integer.parseInt(atomCountStr);
                  totalCount += moleculeCount * atomCount;
              }
          }
          return totalCount;
      }
      

      使用每个拆分结果(->)调用该函数,并比较总计以查看等式是否正确。