Разбор CSV-ввода с RegEx в java

Я знаю, теперь у меня две проблемы. Но я получаю удовольствие!

Я начал с этот совет, чтобы не попробовать и не разбивать, а вместо этого сопоставлять то, что является приемлемым полем, и расширяется оттуда до этого выражение.

final Pattern pattern = Pattern.compile("\"([^\"]*)\"|(?<=,|^)([^,]*)(?=,|$)");

Выражение выглядит следующим образом без раздражающих экранированных кавычек:

"([^"]*)"|(?<=,|^)([^,]*)(?=,|$)

Это хорошо работает для меня - либо оно соответствует "двум кавычкам, и тому, что между ними", либо "что-то между началом строки или запятой, а также концом строки или запятой". Итерация по матчи дает мне все поля, даже если они пусты. Например,

the quick, "brown, fox jumps", over, "the",,"lazy dog"

разбивается на

the quick
"brown, fox jumps"
over
"the"

"lazy dog"

Отлично! Теперь я хочу отбросить кавычки, поэтому я добавил lookahead и lookbehind non-captureing groups, как я делал для запятых.

final Pattern pattern = Pattern.compile("(?<=\")([^\"]*)(?=\")|(?<=,|^)([^,]*)(?=,|$)");

снова выражение:

(?<=")([^"]*)(?=")|(?<=,|^)([^,]*)(?=,|$)

Вместо желаемого результата

the quick
brown, fox jumps
over
the

lazy dog

теперь я получаю этот пробой:

the quick
"brown
 fox jumps"
,over,
"the"
,,
"lazy dog"

Что мне не хватает?

Ответы

Ответ 1

Приоритет оператора. В принципе нет. Все это слева направо. Таким образом, or (|) применяется к закрывающей цитате цитаты и запятой.

Try:

(?:(?<=")([^"]*)(?="))|(?<=,|^)([^,]*)(?=,|$)

Ответ 2

Когда я начал понимать, что я сделал не так, я также начал понимать, как это заставляло задумываться. Я, наконец, понял, что мне не нужен весь согласованный текст, я хотел, чтобы в нем были определенные группы. В итоге я использовал что-то очень похожее на мой оригинальный RegEx, за исключением того, что я не занимался закрывающей запятой, что, по-моему, должно быть немного более эффективным. Вот мой окончательный код.

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);
        }
    }

}

Ответ 3

(?:^|,)\s*(?:(?:(?=")"([^"].*?)")|(?:(?!")(.*?)))(?=,|$)

Это должно делать то, что вы хотите.

Пояснение:

(?:^|,)\s*

Шаблон должен начинаться с символа или начала строки. Кроме того, сначала игнорируйте все пробелы.

Lookahead и посмотрите, будет ли остальное начинаться с цитаты

(?:(?=")"([^"].*?)")

Если это так, то не с жадностью до следующей цитаты.

(?:(?!")(.*?))

Если это не начинается с цитаты, то не следует жадности до следующей запятой или концом строки.

(?=,|$)

Образец должен заканчиваться запятой или концом строки.

Ответ 4

Я знаю, что это не то, чего хочет OP, но для других читателей один из методов String.replace можно использовать для выделения кавычек из каждого элемента в массиве результатов текущего регулярного выражения OP.