Java - 根据数字和字母拆分字符串

12

比如我有这样一个字符串:C3H20IO

我想要将这个字符串拆分成以下几部分:

Array1 = {C,H,I,O}
Array2 = {3,20,1,1}
Array2的第三个元素为1,说明I元素是单原子的。同理可得O也是单原子的。这是一个化学方程式,我需要按照各自元素的名称和原子数量进行分离等操作,这一点让我感到困难。

2
你是如何在array2中得到最后两个条目1,1的?因为你的输入字符串C3H20IO并没有它们。 - Clement Amarnath
棘手的部分是单原子组件不会在数字之前处理。我很想看到一个流畅的Java 8流解决方案。 - Tim Biegeleisen
2
@ClementAmarnath,看起来1,1表示单原子的IO - rock321987
没错。单原子数应该得到1,但实际上并没有数字。我在考虑在toCharArray之后使用for循环,但对于单原子组分还不确定。 - Azazel
1
@Azazel 看看我使用“Map”写的答案。希望能对你有所帮助! - mmuzahid
显示剩余4条评论
10个回答

10
你可以尝试这个方法:
String formula = "C3H20IO";

//insert "1" in atom-atom boundry 
formula = formula.replaceAll("(?<=[A-Z])(?=[A-Z])|(?<=[a-z])(?=[A-Z])|(?<=\\D)$", "1");

//split at letter-digit or digit-letter boundry
String regex = "(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)";
String[] atoms = formula.split(regex);

输出:

原子: [C, 3, H, 20, I, 1, O, 1]

现在,所有偶数索引(0、2、4...)都是原子,奇数索引是相应的数量:

String[] a = new String[ atoms.length/2 ];
int[] n = new int[ atoms.length/2 ];

for(int i = 0 ; i < a.length ; i++) {
    a[i] = atoms[i*2];
    n[i] = Integer.parseInt(atoms[i*2+1]);
}

输出:

a:[C,H,I,O]
n:[3,20,1,1]


5
请记住,原子名称可以用多个字母缩写表示:Ag、Au、Mg等。 - AmazingDreams
@AmazingDreams 这是个好观点,我修正了代码(假设第二个字母小写的惯例得到遵守),通过仅在两个大写字母之间插入“1”来实现。 - Maljam
我正在使用这个,但不幸的是遇到了一个小问题。在某些情况下,n[i] = Integer.parseInt(atoms[i*2+1]) 会抛出 NumberFormatException 异常。我不确定原因。你有任何想法吗? - Azazel
@Azazel,你能给我们展示一些出现该异常的例子吗? - Maljam
我一直在尝试重新创建它,但遇到了麻烦。我将进行更多的强化测试,并在找到示例时通知您。 - Azazel
显示剩余2条评论

4
您可以使用正则表达式通过使用 Matcher.find() 方法滑动输入。
以下是一个大致的示例:
    String input = "C3H20IO";

    List<String> array1 = new ArrayList<String>();
    List<Integer> array2 = new ArrayList<Integer>();

    Pattern pattern = Pattern.compile("([A-Z][a-z]*)([0-9]*)");
    Matcher matcher = pattern.matcher(input);               
    while(matcher.find()){
        array1.add(matcher.group(1));

        String atomAmount = matcher.group(2);
        int atomAmountInt = 1;
        if((atomAmount != null) && (!atomAmount.isEmpty())){
            atomAmountInt = Integer.valueOf(atomAmount);
        }
        array2.add(atomAmountInt);
    }

我知道,缺少从List到Array的转换,但这应该能让你了解如何解决问题。


4

一种不使用REGEX,并且使用ArrayList来存储数据的方法:

String s = "C3H20IO";

char Chem = '-';
String val = "";
boolean isFisrt = true;
List<Character> chemList = new ArrayList<Character>();
List<Integer> weightList = new ArrayList<Integer>();
for (char c : s.toCharArray()) {
    if (Character.isLetter(c)) {
        if (!isFisrt) {
            chemList.add(Chem);
            weightList.add(Integer.valueOf(val.equals("") ? "1" : val));
            val = "";
        }
        Chem = c;
    } else if (Character.isDigit(c)) {
        val += c;
    } 
    isFisrt = false;
}
chemList.add(Chem);
weightList.add(Integer.valueOf(val.equals("") ? "1" : val));

System.out.println(chemList);
System.out.println(weightList);

输出:

[C, H, I, O]
[3, 20, 1, 1]

2

假设每个元素都以大写字母开头,比如你有"Fe",不要在字符串中表示为"FE"。基本上,您需要在每个大写字母处拆分字符串,然后按字母和数字拆分每个新字符串,如果新的拆分不包含数字,则添加"1"。

        String s = "C3H20IO";
        List<String> letters = new ArrayList<>();
        List<String> numbers = new ArrayList<>();

        String[] arr = s.split("(?=\\p{Upper})");  // [C3, H20, I, O]
        for (String str : arr) {  //[C, 3]:[H, 20]:[I]:[O]
            String[] temp = str.split("(?=\\d)", 2);
            letters.add(temp[0]);
            if (temp.length == 1) {
                numbers.add("1");
            } else {
                numbers.add(temp[1]);
            }
        }
        System.out.println(Arrays.asList(letters)); //[[C, H, I, O]]
        System.out.println(Arrays.asList(numbers)); //[[3, 20, 1, 1]]

.split()中,你可以使用第二个参数来限制结果的数量,所以你的第二个分割可以简化为temp = str.split("(?=\\d)", 2) - Alex Salauyou

1
使用输入长度创建(for循环),并添加以下条件。
if(i==number)
// add it to the number array

if(i==character)
//add it into character array

好的。明白了。我已经完成了那个。主要问题在于单原子组件。 - Azazel

1
我建议使用零宽先行断言正则表达式通过大写字母进行分割(以提取像C12O2Si这样的项目),然后将每个项目分成元素和其数值重量:
List<String> elements = new ArrayList<>();
List<Integer> weights = new ArrayList<>();

String[] items = "C6H12Si6OH".split("(?=[A-Z])");  // [C6, H12, Si6, O, H]
for (String item : items) {
    String[] pair = item.split("(?=[0-9])", 2);    // e.g. H12 => [H, 12], O => [O]
    elements.add(pair[0]);
    weights.add(pair.length > 1 ? Integer.parseInt(pair[1]) : 1);
}
System.out.println(elements);  // [C, H, Si, O, H]
System.out.println(weights);   // [6, 12, 6, 1, 1]

1
我按照以下方式进行了这个操作。
ArrayList<Integer> integerCharacters = new ArrayList();
ArrayList<String> stringCharacters = new ArrayList<>();

String value = "C3H20IO"; //Your value 
String[] strSplitted = value.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)"); //Split numeric and strings

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

    if (Character.isLetter(strSplitted[i].charAt(0))){
        stringCharacters.add(strSplitted[i]); //If string then add to strings array
    }
    else{
        integerCharacters.add(Integer.parseInt(strSplitted[i])); //else add to integer array
    }
}

1

您觉得这样行吗?(不使用split

正则表达式演示

String line = "C3H20ZnO2ABCD";
String pattern = "([A-Z][a-z]*)(((?=[A-Z][a-z]*|$))|\\d+)";

Pattern r = Pattern.compile(pattern);

Matcher m = r.matcher(line);

while (m.find( )) {
     System.out.print(m.group(1));
     if (m.group(2).length() == 0) {
         System.out.println(" 1");
     } else {
         System.out.println(" " + m.group(2));
     }
  }

IDEONE DEMO

该文本涉及编程相关内容,保留了HTML格式,并且没有进行解释。

0

你可以使用两个模式:

  • [0-9]
  • [a-zA-Z]

通过它们分别进行两次拆分。

List<String> letters = Arrays.asList(test.split("[0-9]"));
List<String> numbers = Arrays.asList(test.split("[a-zA-Z]"))
            .stream()
            .filter(s -> !s.equals(""))
            .collect(Collectors.toList());

if(letters.size() != numbers.size()){
        numbers.add("1");
    }

单个原子的情况怎么办,例如在 H_2_O 中氧原子后面没有数字。 - Tim Biegeleisen
这实际上是我面临的主要问题。我发现使用那些正则表达式模式的Split会起作用,但这很痛苦。 - Azazel
只有最后一个原子可以没有数字吗? - abyversin

0
你可以使用正则表达式(?<=\D)(?=\d)来拆分字符串。尝试一下这个:
String alphanum= "abcd1234";
String[] part = alphanum.split("(?<=\\D)(?=\\d)");
System.out.println(part[0]);
System.out.println(part[1]);

将输出

abcd 1234


单原子组件怎么样? - Azazel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接