我有一串,其中包含的数字和字母。我希望分割的串入毗连区块的数字和毗连区块的字母。

考虑String"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两次获得一个阵列含有所有的数字大块和一个阵列含有所有的字母块。然后合并的结果。我回避这一点,因为它会损害的可读性。

我有故意避开解决这一regex模式,因为我找到regex patterns是一个主要障碍的可读性。

  • 调试器,不处理它们。
  • 他们中断流动的人阅读的源代码。
  • 加班regex的有机生长,并成为怪物。
  • 他们都深深的非常直观。

我的问题是:

  • 我怎么可能改进的可读性上的代码?
  • 有没有更好的方式做到这一点?一类工具,解决这一问题的优雅。
  • 你在哪里画的线之间使用regEx和编码的东西simpilar为什么我写了上述的?
  • 你如何增加可读性/可维护性的regex?
有帮助吗?

解决方案

你会愿意使用regex如果这意味着解决该问题在一行代码吗?

// 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)");

注释解释regex,我认为这是更具可读性比任何非regex解决方案(或任何其他regex解决方案,就此而言)。

其他提示

对于这个特定的任务,我一直使用一个regex而不是手写类似的东西。代码你有鉴上述,至少对我来说,不太可读于一个简单的规则的表达(这将是 (\d+|[^\d]+) 在这种情况下,作为我可以看见)。

你可能想避免编写经常的表情,超过几行。那些可以并且通常都是无法读取和难以理解, 但这样的代码,他们可以被替换。 分析程序几乎从来不漂亮,你从最好的阅读原文法比设法有意义的产生(或手写)分析器。同样(恕我直言)用于regex这只是一个简明扼要介绍的一个正规语法。

因此,在一般情况下我会说,禁止regex赞成的代码,就像你给你个问题听起来就像一个非常愚蠢的想法。和经常表达的只是一个工具,无所不及,没有什么更多。如果别的东西没一个更好的工作的文本分析(就是说,一个真正的分析器、一些substring神奇,等等。) 然后使用它。但不要丢掉的可能性只是因为你觉得不舒服他们–其他人可有不少问题,应对他们所有人都能够学习。

编辑:更新regex后评论通过mmyers.

对于一个实用工具类,检查了 java。工具.扫描仪.有一定数量的选择在那里,你怎么可能会去解决你的问题。我有一些意见您的问题。

调试器,不处理(经常表达的)好

是否regex工作或不取决于什么是在你的数据。有一些很好的插件你可以用它来帮助你们建立一个regex样 QuickREx 为食,没有一个调试器,实际上帮助您编写正确解析数据的?

他们中断流动的人阅读的源代码。

我想这取决于如何舒适的你是他们。就个人而言,我想宣读一个合理的regex于50多行的串分析代码,但也许这就是一个人的事情。

加班regex的有机生长,并成为怪物。

我猜他们可能,但这可能是一个问题码他们生活在成为庞杂.如果复杂的源数据的不断增加,你可能需要保持眼睛你是否需要一个更富有表现力的方案(可能分析发电机等这些代码)

他们都深深的非常直观。

他们一模式匹配的语言。我会说他们是非常直观,在这种情况。

我怎么可能改进的可读性上的代码?

不知道,除了使用regex.

有没有更好的方式做到这一点?一类工具,解决这一问题的优雅。

上所述,java。工具.扫描仪。

你在哪里画的线之间使用regEx和编码的东西simpilar为什么我写了上述的?

我个人使用regex的任何合理简单。

你如何增加可读性/可维护性的regex?

仔细想想之前延伸,采取额外照顾的评论的代码和regex详细,以便它清楚你在做什么。

我会用这样的东西(警告,未经测试的代码)。对我来说这是一个很大的可读性比试图避免regexp.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;
}

我不过分疯狂的约regex自己,但这似乎是一个情况下,他们会真的事情简单化。什么你可能要做的就是把它们放到最小的方法可以制定,名称很恰当,然后把所有的控制代码在另一个方法。

例如,如果你编码"抓块的数字或字母"的方法,呼叫者将是一个非常简单、直接的环只是打印的结果每次呼叫,并且该方法你打电话会很好的定义这样的意图regex将是明确的,甚至如果你什么都不知道有关语法,且该方法将是界,使人们不可能放弃它随着时间的推移。

这个问题是,regex工具以简单和良好的适应这种使用,这是很难证明一个方法,呼吁这一点。

由于没有一个似乎已经发表正确的代码然而,我会给它一个镜头。

第一个非regex版本。注意我用StringBuilder用于累积的任何类型的角色是看到最后的(数字或非数).如果状态的变化,我倾其内容列入名单,并开始一个新的StringBuilder.这种方式连续非数字分组的就像个连续的数字。

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

现在regex版本。这基本上是同样的代码发布的尤哈S.,但regex的实际工作。

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

一个我试图保持我regex可读是他们的名字。我认为 DIGIT_OR_NONDIGIT_STRING 传达得很好什么我(的编程人员)认为,测试应该确保它真的没什么的意思是要做。

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]

噢,有人打我的代码。我认为regex版本更容易阅读/维护。此外,注意到差异的产出之间的2的实现与期望输出...

输出:

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]

比较:

DigitsAsElements.java:

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DigitsAsElements {

    static List<String> digitsAsElements1(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;
      }

    static List<String> digitsAsElements2(String str){
        // Match a consecutive series of digits or non-digits
        final Pattern pattern = Pattern.compile("(\\d+|\\D+)");
        final Matcher matcher = pattern.matcher(str);

        final List<String> output = new ArrayList<String>();
        while (matcher.find()) {
            output.add(matcher.group());
        }

        return output;
      }

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("digitsAsElements(\"34A312O5MNI444123A\") = " +
                digitsAsElements1("34A312O5MNI444123A"));
        System.out.println("digitsAsElements2(\"34A312O5MNI444123A\") = " +
                digitsAsElements2("34A312O5MNI444123A"));
        System.out.println("Expected: [" +
                "34, A, 312, O, 5, MN, 444123, A"+"]");
    }

}

你可以使用这种类,以便简化循环:

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)) {
    ...
}

我2美分

顺便说一句,此类也可重复使用在其他背景下。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top