在某些情况下使用双引号解析 CSV
Posted
技术标签:
【中文标题】在某些情况下使用双引号解析 CSV【英文标题】:Parse CSV with double quote in some cases 【发布时间】:2011-12-09 16:08:47 【问题描述】:我有带有格式的 csv:
a1, a2, a3, "a4,a5", a6
只有带 , 的字段才会有引号
使用Java,如何轻松解析这个?我尽量避免使用开源 CSV 解析器作为公司政策。谢谢。
【问题讨论】:
不知道很容易,CSV 有一些微妙的边缘情况:转义引号 - 使用多种样式;和字段值中的换行符——如果你必须用它们发生的 CSV 行报告错误,这很有趣。如果您不能使用现有的解析器并且可能不得不处理这些,请编写一个解析器。 (如果不允许使用解析器生成器,这样做也很有趣。) 如果公司要求没有开源库(无论许可证),并且您需要简单解析的帮助... @Inerdia,解析大概30行手写代码,不需要生成器。 Parsing CSV in java 的可能重复项 【参考方案1】:下面的代码似乎运行良好,可以处理引号内的引号。
final static Pattern quote = Pattern.compile("^\\s*\"((?:[^\"]|(?:\"\"))*?)\"\\s*,");
public static List<String> parseCsv(String line) throws Exception
List<String> list = new ArrayList<String>();
line += ",";
for (int x = 0; x < line.length(); x++)
String s = line.substring(x);
if (s.trim().startsWith("\""))
Matcher m = quote.matcher(s);
if (!m.find())
throw new Exception("CSV is malformed");
list.add(m.group(1).replace("\"\"", "\""));
x += m.end() - 1;
else
int y = s.indexOf(",");
if (y == -1)
throw new Exception("CSV is malformed");
list.add(s.substring(0, y));
x += y;
return list;
【讨论】:
【参考方案2】:这是给你的一些代码,我希望使用这里的代码不算开源,这是。
package bestsss.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class SplitCSVLine
public static String[] splitCSV(BufferedReader reader) throws IOException
return splitCSV(reader, null, ',', '"');
/**
*
* @param reader - some line enabled reader, we lazy
* @param expectedColumns - convenient int[1] to return the expected
* @param separator - the C(omma) SV (or alternative like semi-colon)
* @param quote - double quote char ('"') or alternative
* @return String[] containing the field
* @throws IOException
*/
public static String[] splitCSV(BufferedReader reader, int[] expectedColumns, char separator, char quote) throws IOException
final List<String> tokens = new ArrayList<String>(expectedColumns==null?8:expectedColumns[0]);
final StringBuilder sb = new StringBuilder(24);
for(boolean quoted=false;;sb.append('\n')) //lazy, we do not preserve the original new line, but meh
final String line = reader.readLine();
if (line==null)
break;
for (int i = 0, len= line.length(); i < len; i++)
final char c = line.charAt(i);
if (c == quote)
if( quoted && i<len-1 && line.charAt(i+1) == quote )//2xdouble quote in quoted
sb.append(c);
i++;//skip it
else
if (quoted)
//next symbol must be either separator or eol according to RFC 4180
if (i==len-1 || line.charAt(i+1) == separator)
quoted = false;
continue;
else//not quoted
if (sb.length()==0)//at the very start
quoted=true;
continue;
//if fall here, bogus, just add the quote and move on; or throw exception if you like to
/*
5. Each field may or may not be enclosed in double quotes (however
some programs, such as Microsoft Excel, do not use double quotes
at all). If fields are not enclosed with double quotes, then
double quotes may not appear inside the fields.
*/
sb.append(c);
else if (c == separator && !quoted)
tokens.add(sb.toString());
sb.setLength(0);
else
sb.append(c);
if (!quoted)
break;
tokens.add(sb.toString());//add last
if (expectedColumns !=null)
expectedColumns[0] = tokens.size();
return tokens.toArray(new String[tokens.size()]);
public static void main(String[] args) throws Throwable
java.io.StringReader r = new java.io.StringReader("222,\"\"\"zzzz\", abc\"\" , 111 ,\"1\n2\n3\n\"");
System.out.println(java.util.Arrays.toString(splitCSV(new BufferedReader(r))));
【讨论】:
【参考方案3】:我遇到了同样的问题(但在 Python 中),我发现解决它的一种方法是: 当你得到该行时,检查任何引号,如果有引号,将字符串拆分为引号,并将结果数组的偶数索引结果拆分为逗号。奇数索引字符串应该是完整的引用值。
我不是 Java 编码员,所以把它当作伪代码...
line = String[];
if ('"' in row)
vals = row.split('"');
for (int i =0; i<vals.length();i+=2)
line+=vals[i].split(',');
for (int j=1; j<vals.length();j+=2)
line+=vals[j];
else
line = row.split(',')
或者,使用正则表达式。
【讨论】:
【参考方案4】:您可以将Matcher.find
与以下正则表达式一起使用:
这里有一个更完整的例子:
String s = "a1, a2, a3, \"a4,a5\", a6";
Pattern pattern = Pattern.compile("\\s*(\"[^\"]*\"|[^,]*)\\s*");
Matcher matcher = pattern.matcher(s);
while (matcher.find())
System.out.println(matcher.group(1));
在线查看:ideone
【讨论】:
更一般地说,在 CSV 文件中,只要包含分隔符、换行符和/或引号,值就会用引号括起来…… @Mark,双引号("")用来表示单个"。另外,使用regExp也不过分 这不起作用,因为它会在元素之间添加空字符串,如果 csv 中有空单元格,则会产生问题。 这是一个更好的答案(不添加空字符串):***.com/a/15739087/1068385以上是关于在某些情况下使用双引号解析 CSV的主要内容,如果未能解决你的问题,请参考以下文章
Boost tokenizer 无法解析具有双引号字段的 csv 文件