Java 解析csv,不要在单引号或双引号内拆分

Java 解析csv,不要在单引号或双引号内拆分,java,csv,opencsv,Java,Csv,Opencsv,我试图用java解析csv,但有以下问题:第二列是一个字符串(可能也包含逗号),用双引号括起来,除非字符串本身包含双引号,否则整个字符串都用单引号括起来。e、 g 线路可能会像这样断断续续: someStuff,"hello", someStuff someStuff,"hello, SO", someStuff someStuff,'say "hello, world"', someStuff someStuff,'say "hello, world', someStuff someStuf

我试图用java解析csv,但有以下问题:第二列是一个字符串(可能也包含逗号),用双引号括起来,除非字符串本身包含双引号,否则整个字符串都用单引号括起来。e、 g

线路可能会像这样断断续续:

someStuff,"hello", someStuff
someStuff,"hello, SO", someStuff
someStuff,'say "hello, world"', someStuff
someStuff,'say "hello, world', someStuff
someStuff是其他元素的占位符,也可以包含相同样式的引号

我正在寻找一种以逗号分隔行的通用方法,除非用单引号或双引号括起来,以便将第二列作为字符串。对于第二列,我指的是字段:

  • 你好
  • 你好,那么
  • 说“你好,世界”
  • 说“你好,世界
我尝试了OpenCSV,但失败了,因为只能指定一种类型的报价:

public class CSVDemo {

public static void main(String[] args) throws IOException {
    CSVDemo demo = new CSVDemo();
    demo.process("input.csv");
}

public void process(String fileName) throws IOException {
    String file = this.getClass().getClassLoader().getResource(fileName)
            .getFile();
    CSVReader reader = new CSVReader(new FileReader(file));
    String[] nextLine;
    while ((nextLine = reader.readNext()) != null) {
        System.out.println(nextLine[0] + " | " + nextLine[1] + " | "
                + nextLine[2]);
    }
}
}

使用opencsv的解决方案在最后一行失败,其中只有一个双引号括在单引号中:

someStuff | hello |  someStuff
someStuff | hello, SO |  someStuff
someStuff | 'say "hello, world"' |  someStuff
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1

opencsv似乎不支持这种开箱即用的方法。您可以扩展
com.opencsv.CSVParser
,并实现自己的算法来处理两种类型的引号。这是您将要更改的方法的来源,下面是一个存根,让您开始

class MyCSVParser extends CSVParser{
    @Override
    private String[] parseLine(String nextLine, boolean multi) throws IOException{
        //Your algorithm here
    }
}

如果你真的不能使用真正的CSV解析器,你可以使用正则表达式。这通常不是一个好主意,因为总有一些边缘情况你不能处理,但是如果格式严格按照你描述的那样,那么这可能会起作用

public void test() {
    String[] tests = {"numeStuff,\"hello\", someStuff, someStuff",
        "numeStuff,\"hello, SO\", someStuff, someStuff",
        "numeStuff,'say \"hello, world\"', someStuff, someStuff"
    };
    /* Matches a field and a potentially empty separator.
     *
     *  ( - Field Group
     *     \"  - Start with a quote
     *     [^\"]*? - Non-greedy match on anything that is not a quote
     *     \" - End with a quote
     *   | - Or
     *     '  - Start with a strop
     *     [^']*? - Non-greedy match on anything that is not a strop
     *     ' - End with a strop
     *   | - Or
     *    [^\"'] - Not starting with a quote or strop
     *    [^,$]*? - Non-greedy match on anything that is not a comma or end-of-line
     *  ) - End field group
     *  ( - Separator group
     *   [,$] - Comma separator or end of line
     *  ) - End separator group
     */
    Pattern p = Pattern.compile("(\"[^\"]*?\"|'[^\']*?\'|[^\"'][^,\r\n]*?)([,\r\n]|$)");
    for (String t : tests) {
        System.out.println("Matching: " + t);
        Matcher m = p.matcher(t);
        while (m.find()) {
            System.out.println(m.group(1));
        }
    }
}

opencv似乎不支持这一点。不过,看看前面的问题和我的答案以及其他答案,以防它们有所帮助 你:

下面的示例中,请不要
notInsideComma
实际上是指“内部引号”。下面的代码可以扩展以检查引号和双引号

public static ArrayList<String> customSplitSpecific(String s)
{
    ArrayList<String> words = new ArrayList<String>();
    boolean notInsideComma = true;
    int start =0, end=0;
    for(int i=0; i<s.length()-1; i++)
    {
        if(s.charAt(i)==',' && notInsideComma)
        {
            words.add(s.substring(start,i));
            start = i+1;                
        }   
        else if(s.charAt(i)=='"')
        notInsideComma=!notInsideComma;
    }
    words.add(s.substring(start));
    return words;
}   
publicstaticarraylistcustomsplitspecific(字符串s)
{
ArrayList words=新的ArrayList();
布尔值NotInsideCommand=true;
int start=0,end=0;

对于(int i=0;i基本上您只需要跟踪
,“
,”
(修剪中间的部分)

当您遇到其中一种情况时,请将相应的标志(例如singleQuoteOpen、doubleQuoteOpen)设置为true,以指示它们处于打开状态,并且您处于忽略逗号模式

当满足适当的结束引号时,重置标志并继续切片元素

要执行检查,请在每个逗号处停止(当不处于忽略逗号模式时),并查看下一个字符(如果有)和修剪



注意:regex解决方案很好,也很短,但对于边缘情况(至少没有大麻烦)可定制性较差。

如果每行使用单引号和双引号是一致的,可以选择每行相应的引号类型:

public class CSVDemo {
    public static void main(String[] args) throws IOException {
        CSVDemo demo = new CSVDemo();
        demo.process("input.csv");
    }

    public void process(String fileName) throws IOException {
        String file = this.getClass().getClassLoader().getResource(fileName)
                .getFile();

        CSVParser doubleParser = new CSVParser(',', '"');
        CSVParser singleParser = new CSVParser(',', '\'');

        String[] nextLine;

        try (BufferedReader br = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = br.readLine()) != null) {
                if (line.contains(",'") && line.contains("',")) {
                    nextLine = singleParser.parseLine(line);
                } else {
                    nextLine = doubleParser.parseLine(line);
                }

                System.out.println(nextLine[0] + " | " + nextLine[1] + " | "
                        + nextLine[2]);
            }
        }
    }
}

你能更改数据吗?
“说\“你好,世界\”
应该在opencsv中工作。数据在一个文件中,所以我可以在解析之前更改它…即读取行、更改/转义引号,然后拆分它