Разделение на запятую вне цитат
Моя программа считывает строку из файла. Эта строка содержит разделенный запятой текст, например:
123,test,444,"don't split, this",more test,1
Я бы хотел, чтобы результат разделения был следующим:
123
test
444
"don't split, this"
more test
1
Если я использую String.split(",")
, я бы получил следующее:
123
test
444
"don't split
this"
more test
1
Другими словами: запятая в подстроке "don't split, this"
не является разделителем. Как справиться с этим?
Спасибо заранее.
Jakob
Ответы
Ответ 1
Вы можете попробовать это регулярное выражение:
str.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
Это разделяет строку на ,
, за которой следует четное число двойных кавычек. Другими словами, он разбивается на запятую вне двойных кавычек. Это будет работать, если у вас есть сбалансированные кавычки в вашей строке.
Пояснение:
, // Split on comma
(?= // Followed by
(?: // Start a non-capture group
[^"]* // 0 or more non-quote characters
" // 1 quote
[^"]* // 0 or more non-quote characters
" // 1 quote
)* // 0 or more repetition of non-capture group (multiple of 2 quotes will be even)
[^"]* // Finally 0 or more non-quotes
$ // Till the end (This is necessary, else every comma will satisfy the condition)
)
Вы можете даже ввести такой код в свой код, используя модификатор (?x)
с вашим регулярным выражением. Модификатор игнорирует любые пробелы в вашем регулярном выражении, поэтому становится легче читать регулярное выражение, разбитое на несколько строк:
String[] arr = str.split("(?x) " +
", " + // Split on comma
"(?= " + // Followed by
" (?: " + // Start a non-capture group
" [^\"]* " + // 0 or more non-quote characters
" \" " + // 1 quote
" [^\"]* " + // 0 or more non-quote characters
" \" " + // 1 quote
" )* " + // 0 or more repetition of non-capture group (multiple of 2 quotes will be even)
" [^\"]* " + // Finally 0 or more non-quotes
" $ " + // Till the end (This is necessary, else every comma will satisfy the condition)
") " // End look-ahead
);
Ответ 2
Почему Split, если вы можете сопоставить?
Воскрешая этот вопрос, потому что по какой-то причине простое решение не было упомянуто. Вот наше красиво компактное регулярное выражение:
"[^"]*"|[^,]+
Это будет соответствовать всем желаемым фрагментам (см. демонстрацию).
Объяснение
- С
"[^"]*"
мы сопоставим полный "double-quoted strings"
- или
|
- мы сопоставляем
[^,]+
любые символы, которые не являются запятой.
Возможная доработка заключается в том, чтобы улучшить строчную сторону чередования, чтобы позволить цитированным строкам включать экранированные кавычки.
Ответ 3
Вы можете сделать это очень легко без сложного регулярного выражения:
- Разделить на символ
"
. Вы получаете список строк.
- Обработать каждую строку в списке: Разделить каждую строку, которая находится на четной позиции в Списке (начиная индексирование с нулем) на "," (вы получаете список внутри списка), оставляйте каждую нечетную позиционную строку в одиночку (непосредственно помещая его в список внутри списка).
- Присоединитесь к списку списков, чтобы получить только список.
Если вы хотите обрабатывать цитирование '' ', вам нужно немного адаптировать алгоритм (присоединение к некоторым частям, некорректное разделение или изменение разбиения на простое регулярное выражение), но основная структура остается.
Итак, в основном это что-то вроде этого:
public class SplitTest {
public static void main(String[] args) {
final String splitMe="123,test,444,\"don't split, this\",more test,1";
final String[] splitByQuote=splitMe.split("\"");
final String[][] splitByComma=new String[splitByQuote.length][];
for(int i=0;i<splitByQuote.length;i++) {
String part=splitByQuote[i];
if (i % 2 == 0){
splitByComma[i]=part.split(",");
}else{
splitByComma[i]=new String[1];
splitByComma[i][0]=part;
}
}
for (String parts[] : splitByComma) {
for (String part : parts) {
System.out.println(part);
}
}
}
}
Это будет намного чище с лямбдами, обещано!
Ответ 4
См. ниже фрагмент кода. Этот код учитывает только счастливый поток. Измените в соответствии с вашим требованием
public static String[] splitWithEscape(final String str, char split,
char escapeCharacter) {
final List<String> list = new LinkedList<String>();
char[] cArr = str.toCharArray();
boolean isEscape = false;
StringBuilder sb = new StringBuilder();
for (char c : cArr) {
if (isEscape && c != escapeCharacter) {
sb.append(c);
} else if (c != split && c != escapeCharacter) {
sb.append(c);
} else if (c == escapeCharacter) {
if (!isEscape) {
isEscape = true;
if (sb.length() > 0) {
list.add(sb.toString());
sb = new StringBuilder();
}
} else {
isEscape = false;
}
} else if (c == split) {
list.add(sb.toString());
sb = new StringBuilder();
}
}
if (sb.length() > 0) {
list.add(sb.toString());
}
String[] strArr = new String[list.size()];
return list.toArray(strArr);
}