Java中解析字符串中数字的更好方法的帮助

Java中解析字符串中数字的更好方法的帮助,java,regex,string,readability,literate-programming,Java,Regex,String,Readability,Literate Programming,我有一个包含数字和字母的字符串。我希望将字符串分为连续的数字块和连续的字母块 考虑字符串“34A312O5M444123A” 我想输出: [“34”、“A”、“312”、“O”、“5”、“M”、“444123”、“A”] 我有这样的代码: List<String> digitsAsElements(String str){ StringBuilder digitCollector = new StringBuilder(); List<String> outpu

我有一个包含数字和字母的字符串。我希望将字符串分为连续的数字块和连续的字母块

考虑字符串“34A312O5M444123A”

我想输出: [“34”、“A”、“312”、“O”、“5”、“M”、“444123”、“A”]

我有这样的代码:

List<String> digitsAsElements(String str){
  StringBuilder digitCollector = new StringBuilder();

  List<String> output = new ArrayList<String>();

  for (int i = 0; i < str.length(); i++){
    char cChar = str.charAt(i);

    if (Character.isDigit(cChar))
       digitCollector.append(cChar);
    else{
      output.add(digitCollector.toString());
      output.add(""+cChar);

      digitCollector = new StringBuilder();
    }         
  }

  return output;
}
列出数字元素(字符串str){
StringBuilder digitCollector=新建StringBuilder();
列表输出=新的ArrayList();
对于(int i=0;i
我考虑将str拆分两次,得到一个包含所有数字块的数组和一个包含所有字母块的数组。然后合并结果。我回避这一点,因为它会损害可读性

我故意避免使用正则表达式模式来解决这个问题,因为我发现正则表达式模式是可读性的主要障碍

  • 调试器不能很好地处理它们
  • 它们会中断某人阅读源代码的流程

  • 超时正则表达式有机地生长并变成怪物
  • 它们是非常不直观的
我的问题是:

  • 如何提高上述代码的可读性
  • 有更好的方法吗?一个优雅地解决这个问题的Util类
  • 在使用正则表达式和根据我上面所写的内容编写简单的代码之间,你的界限在哪里

  • 如何提高正则表达式的可读性/可维护性

    • 对于这个特殊任务,我总是使用正则表达式,而不是手写类似的东西。至少对我来说,您上面给出的代码不如简单的正则表达式(据我所见,在本例中是
      (\d++[^\d]+)
      )可读

      您可能希望避免编写超过几行的正则表达式。这些可能是,通常是不可读和难以理解的,但它们可以被替换的代码也是如此!解析器几乎从来都不漂亮,通常阅读原始语法比理解生成的(或手写的)解析器要好。正则表达式也是如此(imho),它只是规则语法的简明描述

      所以,总的来说,我会说禁止正则表达式而支持你在问题中给出的代码听起来是一个非常愚蠢的想法。正则表达式只是一种工具,不多不少。如果其他东西在文本解析方面做得更好(比如,一个真正的解析器,一些子字符串魔术等等),那么就使用它。但不要仅仅因为你对可能性感到不舒服就把它扔掉——其他人在处理这些可能性时可能没有那么多问题,而且所有人都能够学习


      编辑:在mmyers的评论之后更新了正则表达式。

      我会使用类似的东西(警告,未测试的代码)。对我来说,这比试图避免正则表达式更具可读性。在正确的位置使用regexp是一个很好的工具

      注释方法以及在注释中提供输入和输出值的示例也有帮助

      List<String> digitsAsElements(String str){
          Pattern p = Pattern.compile("(\\d+|\\w+)*");
          Matcher m = p.matcher(str);
      
          List<String> output = new ArrayList<String>();
          for(int i = 1; i <= m.groupCount(); i++) {
             output.add(m.group(i));
          }
          return output;
      }
      
      列出数字元素(字符串str){
      Pattern p=Pattern.compile(“\\d+\\w+”);
      匹配器m=p.Matcher(str);
      列表输出=新的ArrayList();
      
      对于(inti=1;iAwww),有人比我更容易编写代码。我认为regex版本更易于阅读/维护。另外,请注意两种实现的输出与预期输出之间的差异

      输出:

      digitsAsElements1("34A312O5MNI444123A") = [34, A, 312, O, 5, M, , N, , I, 444123, A]
      digitsAsElements2("34A312O5MNI444123A") = [34, A, 312, O, 5, MNI, 444123, A]
      Expected: [34, A, 312, O, 5, MN, 444123, A]
      
      比较:

      digitsAsElements1("34A312O5MNI444123A") = [34, A, 312, O, 5, M, , N, , I, 444123, A]
      digitsAsElements2("34A312O5MNI444123A") = [34, A, 312, O, 5, MNI, 444123, A]
      Expected: [34, A, 312, O, 5, MN, 444123, A]
      
      DigitsElements.java:

      import java.util.ArrayList;
      导入java.util.List;
      导入java.util.regex.Matcher;
      导入java.util.regex.Pattern;
      公共类数字要素{
      静态列表digitsAsElements1(字符串str){
      StringBuilder digitCollector=新建StringBuilder();
      列表输出=新的ArrayList();
      对于(int i=0;i
      对于实用程序类,请查看。关于如何解决您的问题,这里有许多选项。我对您的问题有一些评论

      调试器不能很好地处理它们(正则表达式)

      正则表达式是否有效取决于数据中的内容。有一些不错的插件可以帮助您构建正则表达式,比如Eclipse,调试器是否真的可以帮助您为数据编写正确的解析器

      它们会中断某人阅读源代码的流程

      我想这取决于你对它们的适应程度。就我个人而言,我宁愿阅读一个合理的正则表达式,也不愿多读50行字符串解析代码,但这可能是一个错误
      public class StringIterator implements Iterator<Character> {
      
          private final char[] chars;
          private int i;
      
          private StringIterator(char[] chars) {
              this.chars = chars;
          }
      
          public boolean hasNext() {
              return i < chars.length;
          }
      
          public Character next() {
              return chars[i++];
          }
      
          public void remove() {
              throw new UnsupportedOperationException("Not supported.");
          }
      
          public static Iterable<Character> of(String string) {
              final char[] chars = string.toCharArray();
      
              return new Iterable<Character>() {
      
                  @Override
                  public Iterator<Character> iterator() {
                      return new StringIterator(chars);
                  }
              };
          }
      }
      
      for (int i = 0; i < str.length(); i++){
          char cChar = str.charAt(i);
          ...
      }
      
      for (Character cChar : StringIterator.of(str)) {
          ...
      }
      
      static List<String> digitsAsElements(String str) {
          StringBuilder collector = new StringBuilder();
      
          List<String> output = new ArrayList<String>();
          boolean lastWasDigit = false;
          for (int i = 0; i < str.length(); i++) {
              char cChar = str.charAt(i);
      
              boolean isDigit = Character.isDigit(cChar);
              if (isDigit != lastWasDigit) {
                  if (collector.length() > 0) {
                      output.add(collector.toString());
                      collector = new StringBuilder();
                  }
                  lastWasDigit = isDigit;
              }
              collector.append(cChar);
          }
          if (collector.length() > 0)
              output.add(collector.toString());
      
          return output;
      }
      
      private static final Pattern DIGIT_OR_NONDIGIT_STRING =
              Pattern.compile("(\\d+|[^\\d]+)");
      static List<String> digitsAsElementsR(String str) {
          // Match a consecutive series of digits or non-digits
          final Matcher matcher = DIGIT_OR_NONDIGIT_STRING.matcher(str);
          final List<String> output = new ArrayList<String>();
          while (matcher.find()) {
              output.add(matcher.group());
          }
          return output;
      }
      
      public static void main(String[] args) {
          System.out.println(digitsAsElements( "34A312O5MNI444123A"));
          System.out.println(digitsAsElementsR("34A312O5MNI444123A"));
      }
      
      [34, A, 312, O, 5, MNI, 444123, A] [34, A, 312, O, 5, MNI, 444123, A]
      // Split at any position that's either:
      // preceded by a digit and followed by a non-digit, or
      // preceded by a non-digit and followed by a digit.
      String[] parts = str.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)");