Почему \r\n.split(\r\n) возвращает пустой массив?

У меня есть регулярное выражение «[\r\n\f]+», чтобы найти количество строк, содержащихся в строке. Мой код такой:

pattern = Pattern.compile("[\\r\\n\\f]+")
String[] lines = pattern.split(texts);

В моем модульном тесте у меня есть образцы строк, подобные этим:

"\t\t\t    \r\n      \n"
"\r\n"

Результат синтаксического анализа первой строки равен 2, однако он становится равным 0 при синтаксическом анализе второй строки.

Я думал, что вторая строка включает 1 строку, хотя строка «пустая» (предположим, я редактирую файл, начинающийся с «\ r \ n» в текстовом редакторе, следует ли помещать курсор во вторую строку?). Является ли мое регулярное выражение неправильным для синтаксического анализа строк? или я что-то пропустил здесь?

Изменить:

Думаю, я сделаю вопрос более очевидным:

Почему

// notice the trailing space in the string
"\r\n ".split("\r\n").length == 2 // results in 2 strings {"", " "}. So this block of text has two lines.

но

// notice there's no trailing space in the string 
"\r\n".split("\r\n").length == 0 // results in an empty array. Why "" (empty string) is not in the result and this block of text contains 0 lines?

person Yu Lu    schedule 30.05.2014    source источник


Ответы (2)


Из документации для Pattern.split(CharSequence)< /а>:

Этот метод работает так, как будто вызывается метод разделения с двумя аргументами с заданной входной последовательностью и предельным аргументом, равным нулю. Таким образом, конечные пустые строки не включаются в результирующий массив.

Многие согласятся, что такое поведение непоследовательно до степени смешения. Вы можете отключить удаление конечных пробелов, включив отрицательный предел (все отрицательные значения делают то же самое):

String[] lines = pattern.split(texts, -1);
person Laurence Gonsalves    schedule 30.05.2014
comment
Ой! Кажется, это ломает все. Разделение \t\t\t \r\n \n дает мне 3, а \r\n дает мне 2, что еще больше сбивает с толку... - person Yu Lu; 31.05.2014
comment
Поведение по умолчанию, заключающееся в отбрасывании завершающих пустых строк, скопировано из Perl's split(). К сожалению, это все, что было скопировано. Одна особенность, которой мне особенно не хватает, — это возможность отбрасывать все пустые строки. - person Alan Moore; 31.05.2014
comment
@Shunshun split имеет дело с разделителями. То есть вещи, которые идут между вещами. Если вам нужны терминаторы (довольно типичные для концов строк), вам нужно игнорировать последний пустой элемент. Если он не пуст, вам нужно решить, ошибка это или нет. Предполагая, что вы не думаете, что это ошибка, что-то вроде этого должно работать: numLines = lines.length; if (lines.length > 0 && lines[lines.length - 1].isEmpty()) numLines--; - person Laurence Gonsalves; 31.05.2014

То, что считается линией, действительно зависит от вашей среды. цитата из википедии:

LF: Multics, Unix и Unix-подобные системы (GNU/Linux, OS X, FreeBSD, AIX, Xenix и др.), BeOS, Amiga, RISC OS и другие.

CR: 8-битные машины Commodore, Acorn BBC, ZX Spectrum, TRS-80, семейство Apple II, Mac OS до версии 9 и OS-9.

RS: реализация QNX до POSIX. 0x9B: 8-битные машины Atari, использующие вариант ASCII ATASCII. (155 в десятичной системе)

LF+CR: Буферизированный текстовый вывод Acorn BBC и RISC OS.

CR+LF: Microsoft Windows, DEC TOPS-10, RT-11 и большинство других ранних операционных систем, отличных от Unix и IBM, CP/M, MP/M, DOS (MS-DOS, PC DOS и т. д.), Atari TOS, OS/2, Symbian OS, Palm OS, Amstrad CPC

Возможно, вам следует попробовать абсолютно нейтральный подход:

    String test = "\t\t\t    \r\n      \n";
    BufferedReader reader = new BufferedReader(new StringReader(test));
    int count = 0;
    String line=null;
    while ((line=reader.readLine()) != null) {
        System.out.println(++count+":"+line);
    }
    System.out.println("total lines == "+count);

Отредактировано, чтобы включить примечание Алана Мура об использовании .ready()

person Andreas    schedule 30.05.2014
comment
Это дает то, что я хочу (с небольшой модификацией. Циклы while - бесконечный цикл). Однако это единственный способ правильно разбить блок текста по строкам без использования какой-либо сторонней библиотеки? И говоря о сторонних библиотеках (например, Apache Common), какое регулярное выражение они используют для разделения строк? - person Yu Lu; 31.05.2014
comment
Я не уверен, что это единственный правильный способ, и даже если это самый правильный способ, но он должен переноситься на все окончания строк. Похоже, что readLine() (согласно документам java 1.7) ищет any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed. Вы должны быть в состоянии сделать регулярное выражение из этого. Если через несколько лет новый формат файла будет широко использоваться, ваше регулярное выражение может больше не работать. BufferedReader.readLine() (надеюсь) еще будет. - person Andreas; 31.05.2014
comment
ну, не уверен, отвечает ли это на мой вопрос или нет. Пожалуйста, смотрите мой обновленный вопрос выше. - person Yu Lu; 31.05.2014
comment
OTBI (не по теме, но важно): метод ready() предназначен не для этого. Подробности см. в этом вопросе. - person Alan Moore; 31.05.2014