(Java) 如何使用 .matches() 检查字符串中的两位或多位整数?

(Java) How do you check a string for two or more digit integers using .matches()?

此程序的 objective 是 return 来自给定公式(作为字符串)的原子质量,使用包含元素及其原子质量的 CSV 文件。

我针对这个特定问题的代码是这样的:

    double mass = 0.0;

    Map<String, Double> perTableMap = fileReader(periodicTable);

    Map<String, Integer> atomMap = getAtoms(formula);

    for (Map.Entry<String, Double> periEntry : perTableMap.entrySet()) {
        for (Map.Entry<String, Integer> atomEntry : atomMap.entrySet()) {
            if (atomEntry.getKey().equals(periEntry.getKey())) {
                mass += periEntry.getValue() * atomEntry.getValue();
            }
        }
    }

    return mass;

我有另一种方法“fileReader”,它从英里获取数据,returns 一个地图,其中元素作为键,质量作为值,这个工作正常。

此代码也适用于原子数为个位数的“公式”,例如“OCS”、“C4H4AsH”和“Fe2O3”。

但是,当原子数量为2位或更多位数字时,它只读取该数字的第一位数字。 例如:对于“RuSH2112”,它应该是 Ru1 + S1 + H2112,但输出是 Ru1 + S1 + H2.

我认为我的方法“getAtoms”有问题,它说“!!! 这里!!!”,这是它的代码:

public static Map<String, Integer> getAtoms(String formula) {

    // LinkedHashMap stores in insertion order
    Map<String, Integer> newMap = new LinkedHashMap<>();

    for (int i = 0; i < formula.length(); i++) {
        int count = 0;

        // convert string to char
        char c = formula.charAt(i);

        // convert char to string
        String a = String.valueOf(c);

        // check formula for upper case values
        if (a.matches("[A-Z]")) {

            for (int j = i + 1; j < formula.length(); j++) {
                char d = formula.charAt(j);
                String b = String.valueOf(d);

                // check formula for lower case values
                if (b.matches("[a-z]")) {
                    a += b;
                    if (newMap.get(a) == null)
                        newMap.put(a, 1);
                    else
                        newMap.put(a, newMap.get(a) + 1);
                    count = 1;
                }

                // check formula for integer values (the end of each molecule)

                // !!! HERE !!!
                else if (b.matches("[\d]")) {
                    int k = Integer.parseInt(b);
                    newMap.put(a, k);
                    count = 1;
                }

                else {
                    i = j - 1;
                    break;
                }
            }

            // put values into a map
            if (count == 0) {
                if (newMap.get(a) == null)
                    newMap.put(a, 1);
                else
                    newMap.put(a, newMap.get(a) + 1);
            }
        }
    }
    return newMap;
}

是否有另一种说法 .matches("[\d]")) 因为我认为它只使用一个数字?

我已经重新实现了你的方法getAtom()。对它所做的主要更改是,它不是逐个字符地处理 formula,而是将 formula 分成代表大写字母的块, 大小写字母的组合,或数字。

这是它的代码:

String[] elementsAndIndices = formula.split("(?<=\p{Lower})(?=\p{Upper})|(?<=\p{Upper})(?=\p{Upper})|(?<=\D)(?=\d)|(?<=\d)(?=\D)");

让我们来看看这里发生了什么:

  • \d - 一个数字:[0-9];
  • \D - 一个 non-digit: [^0-9];
  • \p{Lower} - lower-case 字母字符:[a-z];
  • \p{Upper} - 一个 upper-case 字母字符:[A-Z];
  • \p{Alpha} - 一个字母字符:[\p{Lower}\p{Upper}];

问号开头的特殊构造称为lookbehind?<=\p{Lower})和lookahead (?=\p{Upper})。两者都匹配 zero-length string 允许以我上面描述的方式拆分 formula 而不会丢失任何符号(您可以阅读更多关于他们的信息 here).

用于拆分公式的lookbehind和lookbehind组合的含义:

  • (?<=\p{Lower})(?=\p{Upper}) - 匹配 lower-case 字符 [=18= 之间的 zero-length 字符串] 和一个 upper-case 字符 \p{Upper};

  • (?<=\p{Upper})(?=\p{Upper}) - 匹配两个 upper-case 字符之间边界上的 zero-length 字符串 \p{Upper};

  • (?<=\D)(?=\d) - 匹配 non-digit \D 和数字 [=22 之间的 zero-length 字符串=];

  • (?<=\d)(?=\D) - 匹配数字 \d 和 non-digit [=21 之间的 zero-length 字符串=].

方法merge()用于更新地图。它需要三个参数:键、值和重映射函数。如果给定的键是 absent(或者这个键与 null 值相关联)那么这个键将与 given 相关联值。用于合并两个值的重新映射函数将 仅评估 如果提供的键已经存在并且它的值为 not null .

elements.merge(elementsAndIndices[i], 1, Integer::sum);

上面显示的代码行将值 1 与键 elementsAndIndices[i] 相关联,如果它不存在于地图中,否则,它会将现有值与 1 合并生成和。方法参考 Integer::sum 等价于 lambda (val1, val2) -> val1 + val2;.

    public static Map<String, Integer> getAtoms(String formula) {
        Map<String, Integer> elements = new LinkedHashMap<>();
        String[] elementsAndIndices = 
                formula.split("(?<=\p{Lower})(?=\p{Upper})|(?<=\p{Upper})(?=\p{Upper})|(?<=\D)(?=\d)|(?<=\d)(?=\D)");

        for (int i = 0; i < elementsAndIndices.length; i++) {
            if (elementsAndIndices[i].matches("\p{Alpha}+")) {
                if (i == elementsAndIndices.length - 1 || !elementsAndIndices[i + 1].matches("\d+")) {
                    elements.merge(elementsAndIndices[i], 1, Integer::sum);
                } else {
                    elements.merge(elementsAndIndices[i], Integer.parseInt(elementsAndIndices[i + 1]), Integer::sum);
                    i++;
                }
            }
        }
        return elements;
    }
    public static void main(String[] args) {
        System.out.println(getAtoms("C6H12O6"));
        System.out.println(getAtoms("NH3"));
        System.out.println(getAtoms("RuSH2112"));
    }

输出

{C=6, H=12, O=6}
{N=1, H=3}
{Ru=1, S=1, H=2112}