在 Java 中使用 RegEx 解析 CSV 输入

Posted

技术标签:

【中文标题】在 Java 中使用 RegEx 解析 CSV 输入【英文标题】:Parsing CSV input with a RegEx in java 【发布时间】:2010-11-29 07:39:03 【问题描述】:

我知道,现在我有两个问题。但我玩得很开心!

我从this advice 开始,不是尝试拆分,而是匹配可接受的字段,然后从那里扩展到这个表达式。

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"

我错过了什么?

【问题讨论】:

我假设您的文本本身不能包含引号? 谢天谢地没有。那时我只会使用 openCSV 库。 其他用于 Java 的 CSV 库:***.com/questions/101100/csv-api-for-java 【参考方案1】:

运算符优先级。基本上没有。都是从左到右。所以 or (|) 适用于闭引号前瞻和逗号前瞻

试试:

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

【讨论】:

啊,我明白了。所以我应该尝试将引用的东西和逗号的东西组合在一起。不幸的是,将引号内容包含在 (?: ) 中似乎没有任何效果。我也尝试将它添加到逗号内容中,并且只是将它们分组在另一组括号中,但没有任何效果。我将继续寻找正确的语法;如果我在其他人发布之前找到它,我会给你答案。【参考方案2】:
(?:^|,)\s*(?:(?:(?=")"([^"].*?)")|(?:(?!")(.*?)))(?=,|$)

这应该做你想做的。

解释:

(?:^|,)\s*

模式应该以 , 或字符串开头。另外,忽略开头的所有空格。

向前看,看看其余部分是否以引号开头

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

如果是,则不贪婪地匹配直到下一个引用。

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

如果它不以引号开头,则非贪婪匹配直到下一个逗号或字符串结尾。

(?=,|$)

模式应该以逗号或字符串结尾。

【讨论】:

这个解决方案对我有用,而不是选择的答案,谢谢! 如果字符串以双双引号开头呢?例如: ”””,”,,,,””””。 (无点)【参考方案3】:

当我开始了解我做错了什么时,我也开始了解环顾四周是多么令人费解。我终于意识到我不想要所有匹配的文本,我想要其中的特定组。我最终使用了与我原来的 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);
        
    


【讨论】:

我觉得我应该重申一下,这只是为了我的娱乐,不能保证有效,如果您尝试将转义的分隔符包含在您的某个字段中,肯定不会有效。如果您需要“真实”的东西,请在 sourceforge 或任何地方使用开源 java csv 库。【参考方案4】:

我知道这不是 OP 想要的,但对于其他读者,可以使用 String.replace 方法之一从 OP 当前正则表达式的结果数组中的每个元素中去除引号。

【讨论】:

以上是关于在 Java 中使用 RegEx 解析 CSV 输入的主要内容,如果未能解决你的问题,请参考以下文章

使用 Apache Spark 和 Java 将 CSV 解析为 DataFrame/DataSet

使用Java开发 接收一个Json文件,然后解析Json 并按照特定的Csv格式转换,输出Csv格式的文件,求案例谢谢

在 Powershell 中使用 REGEX 解析 SWIFT(财务)消息字符串

如何使用 Java Regex 查找字符串中的所有重复字符序列?

Java:使用OpenCSV解析CSV文件

java处理csv文件