Создание токенизатора с использованием метода Split - PullRequest
0 голосов
/ 26 октября 2018

Я пытаюсь создать простой токенайзер, который разбивает пробелы, строчные токены, удаляет все неалфавитные символы и сохраняет только термины с 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());
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 27 октября 2018

Если вам не нужно отслеживать индекс:

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<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());
}
0 голосов
/ 27 октября 2018

Так как ваше требование нигде не говорит "max 20", измените [a-z]{3,20} на [a-z]{3,} для неограниченной длины.

Regex не может строчить токены, поэтому вам нужно вызвать 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] можно удалить, так как + в конце будет делатьповторение в любом случае, но регулярное выражение должно работать лучше с + там.

Разница между исходным кодом и кодом разделения состоит в том, что split возвратит пустую строку в качестве первого результата, если ввод начинается снеалфавитный символ.

...