Анализ входных данных CSV с помощью регулярного выражения в java
Вопрос
Я знаю, теперь у меня есть две проблемы.Но мне весело!
Я начал с этот совет не пытаться разделить, а вместо этого сопоставить то, что является приемлемым полем, и расширить оттуда до этого выражения.
final Pattern pattern = Pattern.compile("\"([^\"]*)\"|(?<=,|^)([^,]*)(?=,|$)");
Выражение выглядит примерно так без раздражающих экранированных кавычек:
"([^"]*)"|(?<=,|^)([^,]*)(?=,|$)
Для меня это работает хорошо - либо оно совпадает с "двумя кавычками и тем, что находится между ними", либо "чем-то между началом строки или запятой и концом строки или запятой".Перебирая совпадения, я получаю все поля, даже если они пусты.Например,
the quick, "brown, fox jumps", over, "the",,"lazy dog"
распадается на
the quick
"brown, fox jumps"
over
"the"
"lazy dog"
Отлично!Теперь я хочу убрать кавычки, поэтому я добавил группы "Вперед" и "Назад" без захвата, как я делал для запятых.
final Pattern pattern = Pattern.compile("(?<=\")([^\"]*)(?=\")|(?<=,|^)([^,]*)(?=,|$)");
опять же, это выражение таково:
(?<=")([^"]*)(?=")|(?<=,|^)([^,]*)(?=,|$)
Вместо желаемого результата
the quick
brown, fox jumps
over
the
lazy dog
теперь я получаю этот срыв:
the quick
"brown
fox jumps"
,over,
"the"
,,
"lazy dog"
Что я упускаю из виду?
Решение
Приоритет оператора.По сути, такового нет.Это все слева направо.Таким образом, or (|) применяется к контрольной точке в заключительной кавычке и контрольной точке в запятой
Попробуй:
(?:(?<=")([^"]*)(?="))|(?<=,|^)([^,]*)(?=,|$)
Другие советы
(?:^|,)\s*(?:(?:(?=")"([^"].*?)")|(?:(?!")(.*?)))(?=,|$)
Это должно сделать то, что вы хотите.
Объяснение:
(?:^|,)\s*
Шаблон должен начинаться с a , или начала строки.Кроме того, игнорируйте все пробелы в начале.
Посмотрите вперед и посмотрите, начинается ли остальное с цитаты
(?:(?=")"([^"].*?)")
Если это так, то сопоставляйте без жадности до следующей цитаты.
(?:(?!")(.*?))
Если оно не начинается с кавычки, то сопоставляйте без жадности до следующей запятой или конца строки.
(?=,|$)
Шаблон должен заканчиваться запятой или концом строки.
Когда я начал понимать, что я сделал не так, я также начал понимать, насколько запутанными были обходные пути, делающие это возможным.Наконец я понял, что мне нужен не весь согласованный текст, а определенные группы внутри него.В итоге я использовал что-то очень похожее на мое оригинальное регулярное выражение, за исключением того, что я не делал предварительный просмотр закрывающей запятой, которая, я думаю, должна быть немного более эффективной.Вот мой окончательный код.
package regex.parser;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CSVParser {
/*
* This Pattern will match on either quoted text or text between commas, including
* whitespace, and accounting for beginning and end of line.
*/
private final Pattern csvPattern = Pattern.compile("\"([^\"]*)\"|(?<=,|^)([^,]*)(?:,|$)");
private ArrayList<String> allMatches = null;
private Matcher matcher = null;
private String match = null;
private int size;
public CSVParser() {
allMatches = new ArrayList<String>();
matcher = null;
match = null;
}
public String[] parse(String csvLine) {
matcher = csvPattern.matcher(csvLine);
allMatches.clear();
String match;
while (matcher.find()) {
match = matcher.group(1);
if (match!=null) {
allMatches.add(match);
}
else {
allMatches.add(matcher.group(2));
}
}
size = allMatches.size();
if (size > 0) {
return allMatches.toArray(new String[size]);
}
else {
return new String[0];
}
}
public static void main(String[] args) {
String lineinput = "the quick,\"brown, fox jumps\",over,\"the\",,\"lazy dog\"";
CSVParser myCSV = new CSVParser();
System.out.println("Testing CSVParser with: \n " + lineinput);
for (String s : myCSV.parse(lineinput)) {
System.out.println(s);
}
}
}
Я знаю, что это не то, чего хочет OP, но для других читателей один из методов String.replace может быть использован для удаления кавычек из каждого элемента в результирующем массиве текущего регулярного выражения OP.