【问题标题】:java pattern with tab characters带有制表符的 java 模式
【发布时间】:2012-01-03 20:22:03
【问题描述】:

我有一个文件,其行如下:

string1 (tab) sting2 (tab) string3 (tab) string4

我想从每一行得到string3...我现在从这些行中得到的只是string3 在第二个和第三个制表符之间。 有没有可能用像这样的模式来处理它

Pattern pat = Pattern.compile(".\t.\t.\t.");

【问题讨论】:

  • 分词器怎么样?您可以阅读该行,然后使用标记器从选项卡中解析它。像 StringTokenizer line = new StringTokenizer(myString, "\t"); line.nextToken()

标签: java tabs pattern-matching


【解决方案1】:
String string3 = tempValue.split("\\t")[2];

【讨论】:

  • 我会强烈建议您明确检查split 的结果,而不是盲目地假设至少有三个值。如果您确实期望 exactly 四个值,如果超过四个,我可能仍然希望看到一个错误,如果少于三个,我肯定更喜欢显式异常包括有问题的行等信息,而不仅仅是ArrayIndexOutOfBoundsException
  • 我知道这个潜在的异常,但如果他确定输入当然他可以使用它。
【解决方案2】:

听起来你只是想要:

foreach (String line in lines) {
    String[] bits = line.split("\t");
    if (bits.length != 4) {
        // Handle appropriately, probably throwing an exception
        // or at least logging and then ignoring the line (using a continue
        // statement)
    }
    String third = bits[2];
    // Use...
}

(您可以转义字符串,以便正则表达式引擎必须将反斜杠-t 解析为制表符,但您不必这样做。以上工作正常。)

使用正则表达式的内置 String.split 方法的另一种替代方法是 Guava Splitter 类。这里可能没有必要,但值得注意。

编辑:如 cmets 中所述,如果您要重复使用相同的模式,编译单个 Pattern 并使用 Pattern.split 会更有效:

private static final Pattern TAB_SPLITTER = Pattern.compile("\t");

...

String[] bits = TAB_SPLITTER.split(line);

【讨论】:

  • 你应该先看看 Java 的内置库必须提供什么,然后再自己去如此低调。 Java 的东西更优化,更易读。在这种情况下,您至少有 3 或 4 个可用于实现此目的的单行代码。
  • @AndreiBodnarescu:我以什么方式“如此低级”?我和其他人一样使用String.split - 不同之处在于我已经明确展示了处理多行并且我已经验证了预期的零件数量,而不是盲目地采用第三要素。显式验证允许比ArrayIndexOutOfBoundsException 更有用的异常——例如,它可以包含该行。出于兴趣,您是否检查了我的代码实际上在做什么,或者只是假设因为它不是一行,所以它没用?
  • 好吧,好吧,我觉得你在那儿有点联合起来对付我。我看到它的方式问这个问题的人已经知道数组大小以及当您访问数组外部的索引时会发生什么,他也知道正则表达式以及它在 Java 中的工作原理,他唯一缺少的是存在String 类上的 split(String regex) 方法,所以我认为只需指出这一点就可以了。我还觉得,在您添加的额外代码中(在我看来,从不要求)您过于详细而无法断言某些事情。同样,这只是我的意见。
  • @AndreiBodnarescu:两个独立行动的人并不是真正的帮派,是吗?不,当我使用与您相同的方法 (split) 时,您还没有解释为什么您觉得我应该“首先看看 Java 的内置库必须提供什么”。你到底是在建议我应该寻找什么我显然不知道的? Java 的哪一点“优化且更具可读性”?您的第一条评论看起来就像您没有发现我已经在使用split。如果您说您已经正确阅读了我的答案,那么这是一个非常令人困惑的评论,我根本无法理解。
  • 如果您多次执行拆分(例如在循环中逐行解析文件),那么您应该创建一次Pattern,然后使用Pattern.split而不是String.split。跨度>
【解决方案3】:

如果您想要一个仅捕获第三个字段而不捕获其他字段的正则表达式,您可以使用以下内容:

String regex = "(?:[^\\t]*)\\t(?:[^\\t]*)\\t([^\\t]*)\\t(?:[^\\t]*)";
Pattern pattern = Pattern.compile(regex);

Matcher matcher = pattern.matcher(input);
if (matcher.matches()) {
  System.err.println(matcher.group(1));
}

我不知道在解析大文件时这是否会比 split("\\t") 更好。

更新

我很想知道简单拆分与更明确的正则表达式的表现如何,因此我测试了三种不同的解析器实现。

/** Simple split parser */
static class SplitParser implements Parser {
    public String parse(String line) {
        String[] fields = line.split("\\t");
        if (fields.length == 4) {
            return fields[2];
        }
        return null;
    }
}

/** Split parser, but with compiled pattern */
static class CompiledSplitParser implements Parser {
    private static final String regex = "\\t";
    private static final Pattern pattern = Pattern.compile(regex);

    public String parse(String line) {
        String[] fields = pattern.split(line);
        if (fields.length == 4) {
            return fields[2];
        }
        return null;
    }
}

/** Regex group parser */
static class RegexParser implements Parser {
    private static final String regex = "(?:[^\\t]*)\\t(?:[^\\t]*)\\t([^\\t]*)\\t(?:[^\\t]*)";
    private static final Pattern pattern = Pattern.compile(regex);

    public String parse(String line) {
        Matcher m = pattern.matcher(line);
        if (m.matches()) {
            return m.group(1);
        }
        return null;
    }
}

我对同一个百万行文件运行了十次。以下是平均结果:

  • 拆分:2768.8 毫秒
  • 编译拆分:1041.5 毫秒
  • 组正则表达式:1015.5 毫秒

明确的结论是important to compile your pattern,而不是依赖String.split,如果要重复使用的话。

基于此测试,编译的拆分与组正则表达式的结果不是决定性的。并且可能正则表达式可以进一步调整以提高性能。

更新

另一个简单的优化是重用 Matcher,而不是每次循环迭代都创建一个。

static class RegexParser implements Parser {
    private static final String regex = "(?:[^\\t]*)\\t(?:[^\\t]*)\\t([^\\t]*)\\t(?:[^\\t]*)";
    private static final Pattern pattern = Pattern.compile(regex);

    // Matcher is not thread-safe...
    private Matcher matcher = pattern.matcher("");

    // ... so this method is no-longer thread-safe
    public String parse(String line) {
        matcher = matcher.reset(line);
        if (matcher.matches()) {
            return matcher.group(1);
        }
        return null;
    }
}

【讨论】:

    猜你喜欢
    • 2013-08-09
    • 2021-06-20
    • 2019-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多