Используя вертикальный символ пробела в качестве разделителя java-сканера, сканирует пустую строку в каждой конечной строке - PullRequest
1 голос
/ 19 мая 2019

Я пытаюсь отсканировать файл с полями данных, разделенными символом @ или endline, используя класс Scanner в Java. Вот пример входного файла:

Student @ Codey @ Huntting
Student @ Sarah @ Honsinger

Для правильного сканирования входного файла, который выглядит следующим образом, я попытался изменить разделитель на Java-сканере на регулярное выражение "[@\\v]", которое должно соответствовать либо @, либо любому вертикальному пробелу, включая \n и \r согласно этой странице

Вот код, который я использую для проверки:

Scanner scanner = new Scanner(new File("data/initialize.txt"));
int tokenNum = 0;

scanner.useDelimiter("[@\\v]");

while(scanner.hasNext()) {
    System.out.println("Token #" + tokenNum++ + ": " + scanner.next());
}

scanner.close();

Я ожидаю сканирования токенов:

Token #1: Student
Token #2:  Codey 
Token #3:  Huntting
Token #4: Student
Token #5:  Sarah
Token #6:  Honsinger

Но фактически полученные токены:

Token #1: Student
Token #2:  Codey 
Token #3:  Huntting
Token #4
Token #5: Student
Token #6:  Sarah
Token #7:  Honsinger
Token #8: 

Я бы ожидал, что сканер после сканирования Huntting переместится на новую строку после охоты и при следующем вызове input.next() пропустит эту новую строку, но по какой-то причине сканер, кажется, захватывает там пустая строка в конце строки.

Я проверял несколько раз, и в файле нет пробелов после любой из строк. Я пробовал разные шаблоны, такие как [@[\\v]] и [@][\\v], но они всегда дают данные либо с той же ошибкой пустой строки, либо вывод полностью не в порядке.

Ответы [ 3 ]

0 голосов
/ 20 мая 2019

Ваш файл, вероятно, содержит разрывы строк вида \r\n.В этом случае ваш сканер находит разделитель \r и выводит что-либо до \r.Затем он находит разделитель \n и выводит пустой токен между \r и \n, затем продолжает работу после \n.

Чтобы разрешить разрывы \r\n, я предлагаю вам взять \r\n|[@\v]в том же порядке, что и регулярное выражение.Конечно, после побега это становится "\r\n|[@\\v]".

Как Андреас упомянул другое регулярное выражение, которое вы можете использовать, это @|\R, так как \R соответствует любому переводу строки Unicode, включая \r\n вместе.Это, вероятно, даже лучшее решение.

0 голосов
/ 20 мая 2019

Ваша проблема в том, что разрыв строки представляет собой пару \r\n, и \v соответствует им индивидуально.Чтобы повторить это, давайте изменим ваш код, чтобы использовать встроенную строку для тестовых данных:

String input = "Student @ Codey @ Huntting\r\n" +
               "Student @ Sarah @ Honsinger\r\n";
try (Scanner scanner = new Scanner(input).useDelimiter("[@\\v]")) {
    for (int tokenNum = 0; scanner.hasNext(); tokenNum++) {
        System.out.println("Token #" + tokenNum + ": \"" + scanner.next() + "\"");
    }
}

Выход

Token #0: "Student "
Token #1: " Codey "
Token #2: " Huntting"
Token #3: ""
Token #4: "Student "
Token #5: " Sarah "
Token #6: " Honsinger"
Token #7: ""

Один из способов исправить этопопробуйте сначала сопоставить пару \r\n:

useDelimiter("\r\n|[@\\v]")

Вывод

Token #0: "Student "
Token #1: " Codey "
Token #2: " Huntting"
Token #3: "Student "
Token #4: " Sarah "
Token #5: " Honsinger"

Однако это потратит время на проверку \r дважды, так что, возможно, будетлучше использовать встроенный \R (любая последовательность перевода строки Unicode эквивалентна \u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]) :

useDelimiter("@|\\R")

Тот же результат, но более четко отражаеткак вы хотите соответствовать.


Вы, конечно, можете использовать trim() или strip(), чтобы удалить начальные и конечные пробелы, но почему бы не заставить Scanner выполнять работу?Для использования | требуется группа (без захвата), чтобы отделить ее от совпадения пробелов:

useDelimiter("\\s*(?:@|\\R)\\s*")

Выход

Token #0: "Student"
Token #1: "Codey"
Token #2: "Huntting"
Token #3: "Student"
Token #4: "Sarah"
Token #5: "Honsinger"
0 голосов
/ 20 мая 2019

Если я правильно понимаю, мы могли бы просто захотеть удалить @ и пробел после этого, затем заменить его новыми строками и добавить текст перед ним. Может быть, это выражение поможет:

([\S\s]*?)(?:@\s|$)

enter image description here

RegEx

Если это выражение нежелательно, вы можете изменить / изменить выражения в regex101.com .

RegEx Circuit

Вы также можете визуализировать свои выражения в jex.im :

enter image description here

JavaScript Demo

Этот фрагмент показывает, что мы, вероятно, имеем правильное выражение:

const regex = /([\S\s]*?)(?:@\s|$)/gm;
const str = `Student @ Codey @ Huntting
Student @ Sarah @ Honsinger`;
const subst = `\n$1`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);

Java Test

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

final String regex = "([\\S\\s]*?)(?:@\\s|$)";
final String string = "Student @ Codey @ Huntting\n"
     + "Student @ Sarah @ Honsinger";
final String subst = "$1\\n";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);

// The substituted value will be contained in the result variable
final String result = matcher.replaceAll(subst);

System.out.println("Substitution result: " + result);

Если мы хотим удалить новые строки, мы могли бы добавить это во вторую группу захвата, и проблема может быть решена:

([\s\S]+?)(@\s|\n\s|\n|$)

Здесь, во второй группе захвата (@\s|\n\s|\n|$), используя логическое ИЛИ, мы можем исключить символы, которые нам не нужны:

enter image description here

Демо

const regex = /([\s\S]+?)(@\s|\n\s|\n|$)/gm;
const str = `Student @ Codey @ Huntting

Student @ Sarah @ Honsinger                     
`;
const subst = `Token #: $1\n`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);

Java Test

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

final String regex = "([\\s\\S]+?)(@\\s|\\n\\s|\\n|$)";
final String string = "Student @ Codey @ Huntting\n\n"
     + "Student @ Sarah @ Honsinger                     \n";
final String subst = "Token #: $1\\n";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);

// The substituted value will be contained in the result variable
final String result = matcher.replaceAll(subst);

System.out.println("Substitution result: " + result);
...