我尝试用java解析csv并遇到以下问题:第二列是一个用双引号括起来的String(也可能包含逗号),除非字符串本身包含双引号,那么整个字符串附有单引号。 e.g。
行可能会像这样:
someStuff,"hello", someStuff
someStuff,"hello, SO", someStuff
someStuff,'say "hello, world"', someStuff
someStuff,'say "hello, world', someStuff
someStuff是其他元素的占位符,也可以包含相同样式的引号
我正在寻找一种通用的方法来分隔逗号中的行,除非用单个或双引号括起来,以便将第二列作为String。第二列我的意思是字段:
我尝试过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
答案 0 :(得分:2)
如果你真的不能使用真正的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));
}
}
}
答案 1 :(得分:1)
opencsv似乎不支持开箱即用。您可以扩展com.opencsv.CSVParser
并实现自己的算法来处理两种类型的引号。 This是您要更改的方法的来源,这里有一个存根来帮助您入门。
class MyCSVParser extends CSVParser{
@Override
private String[] parseLine(String nextLine, boolean multi) throws IOException{
//Your algorithm here
}
}
答案 2 :(得分:1)
基本上你只需跟踪,"
和,'
(修剪中间的内容)。
当您遇到其中一个时,请将相应的标志(例如,singleQuoteOpen,doubleQuoteOpen)设置为true以指示它们已打开并且您处于忽略逗号模式。
当您遇到相应的结束语时,请重置标志并继续切片元素。
要执行检查,请在每个逗号处停止(当不处于忽略逗号模式时)并查看下一个字符(如果有的话,并修剪)。
注意:正则表达式解决方案很好而且更短,但边缘情况的可定制性较低(至少没有大问题)。
答案 3 :(得分:0)
看起来opencv似乎不支持这一点。但是,请看看上一个问题和我的答案,以及其他答案,以防他们提供帮助 你:https://stackoverflow.com/a/15905916/1688441
下面是一个示例,请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;
}
答案 4 :(得分:0)
如果每行使用单引号和双引号,则可以为每行选择相应的引用类型:
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]);
}
}
}
}