【问题标题】:Creating a tokenizer using the method Split使用 Split 方法创建分词器
【发布时间】:2018-10-26 20:36:23
【问题描述】:

我正在尝试创建简单的标记器,它可以在空格、小写标记上拆分,删除所有非字母字符,并且只保留包含 3 个或更多字符的术语。我编写了这段代码,它可以处理小写、非字母字符,并且只保留 3 个或更多字符。但是我想用split的方法,但是不知道怎么用。请提出一些建议。

public class main {

    public static final String EXAMPLE_TEST = "This Mariana John bar Barr "
        + "12364 FFFFF aaaa a s d f g.";

    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("(\\s[a-z]{3,20})");
        Matcher matcher = pattern.matcher(EXAMPLE_TEST);

        while (matcher.find()) {
            System.out.print("Start index: " + matcher.start());
            System.out.print(" End index: " + matcher.end() + " ");
            System.out.println(matcher.group());
        }
    }
}

【问题讨论】:

  • 1) split 不能小写结果。 --- 2) 您不能使用split“删除所有非字母字符” 而不拆分它们,但您只说“拆分空格” ,例如输入abc1@3xyz 会发生什么?应该返回abcxyz(非字母删除),还是返回abcxyz? --- 如前所述,您的要求是不可能的。
  • 我也很好奇,当您的正则表达式已经满足您的要求时,您为什么要使用split()
  • 因为我被要求使用 split()

标签: java design-patterns split token tokenize


【解决方案1】:

如果您不必跟踪索引:

List<String> processed = Arrays.stream(EXAMPLE_TEST.split(" ")).map(String::toLowerCase)
            .map(s -> s.replaceAll("[^a-z]", "")).filter(s -> s.length() >= 3).collect(Collectors.toList());
for (String s : processed) {
    System.out.println(s);
}

但您的示例输出也显示了索引。然后您必须将其存储在其他容器中(例如 Map):

Map<Integer, String> processed = Arrays.stream(EXAMPLE_TEST.split(" ")).collect(Collectors.toMap(s -> EXAMPLE_TEST.indexOf(s), s -> s.toLowerCase().replaceAll("[^a-z]", "")));
Map<Integer, String> filtered = processed.entrySet().stream().filter(entry -> entry.getValue().length() >= 3).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
for (Map.Entry<Integer, String> entry : filtered.entrySet()) {
    System.out.println("Start index: " + entry.getKey() + " " + entry.getValue());
}

【讨论】:

    【解决方案2】:

    由于您的要求在任何地方都没有说“最多 20 个”,请将 [a-z]{3,20} 更改为 [a-z]{3,} 以无限长度。

    正则表达式不能小写标记,因此您需要单独调用toLowerCase()。只有在调用正则表达式之前这样做,你的正则表达式才能正常工作。如果您打算在调用正则表达式之后对每个令牌调用toLowerCase(),则需要将[a-z] 更改为[a-zA-Z]。最简单的就是以前做。

    上面的意思是你的代码应该修改如下:

    Pattern pattern = Pattern.compile("[a-z]{3,}");
    Matcher matcher = pattern.matcher(EXAMPLE_TEST.toLowerCase());
    

    输出

    Start index: 0 End index: 4 this
    Start index: 5 End index: 12 mariana
    Start index: 13 End index: 17 john
    Start index: 18 End index: 21 bar
    Start index: 22 End index: 26 barr
    Start index: 33 End index: 38 fffff
    Start index: 39 End index: 43 aaaa
    

    要使用 split 执行相同的操作,您需要拆分由非字母字符或最多 2 个连续字母字符组成的任何字符序列。

    String[] split = EXAMPLE_TEST.toLowerCase().split("(?:[^a-z]+|(?<![a-z])[a-z]{1,2}(?![a-z]))+");
    System.out.println(Arrays.toString(split));
    

    输出

    [this, mariana, john, bar, barr, fffff, aaaa]
    

    解释:

    (?:              Start non-capturing repeating group:
       [^a-z]+           Match one or more nonalphabetic characters
     |                 Or
       (?<![a-z])        Not preceded by an alphabetic character
       [a-z]{1,2}        Match 1-2 alphabetic characters
       (?![a-z])         Not followed by an alphabetic character
    )+               Match one or more of the above
    

    注意: [^a-z] 之后的 + 可以删除,因为最后的 + 无论如何都会重复,但正则表达式应该在 + 那里表现更好.

    原始代码和拆分代码的区别在于,如果输入以非字母字符开头,拆分将返回一个空字符串作为第一个结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多