我有一个场景,我必须解析来自不同来源的CSV文件,解析代码非常简单明了。
String csvFile = "/Users/csv/country.csv";
String line = "";
String cvsSplitBy = ",";
try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) {
while ((line = br.readLine()) != null) {
// use comma as separator
String[] country = line.split(cvsSplitBy);
System.out.println("Country [code= " + country[4] + " , name=" + country[5] + "]");
}
} catch (IOException e) {
e.printStackTrace();
}
我的问题来自CSV分隔符字符,我有许多不同的格式,有时它是,
有时它是;
是否有任何方法可以在解析文件之前确定分隔符字符
答案 0 :(得分:5)
univocity-parsers支持自动检测分隔符(也包括行结尾和引号)。只需使用它而不是与您的代码作斗争:
CsvParserSettings settings = new CsvParserSettings();
settings.detectFormatAutomatically();
CsvParser parser = new CsvParser(settings);
List<String[]> rows = parser.parseAll(new File("/path/to/your.csv"));
// if you want to see what it detected
CsvFormat format = parser.getDetectedFormat();
免责声明:我是这个图书馆的作者,我确保涵盖各种角落案例。它的开源和免费(Apache 2.0许可证)
希望这有帮助。
答案 1 :(得分:1)
最简单的答案是拥有一个包含所有可用分隔符的列表,并尝试识别正在使用的字符。即使您必须对文件或创建它们的人/人放置一些限制。请看以下两种情况:
案例1 - file.csv的内容
test,test2,test3
案例2 - file.csv的内容
test1|test2,3|test4
如果您事先了解分隔符字符,那么您将使用,
分割第一个字符串,使用|
分割第二个字符串,得到相同的结果。但是,如果您尝试通过解析文件来识别分隔符,则两个字符串可以使用,
字符进行拆分,您最终会得到:
案例1 - 使用,
test1
test2
test3
案例2 - 使用,
test1|test2
3|test4
由于缺乏使用哪个分隔符的先验知识,您无法创建将解析每个文本组合的“神奇”算法;即使是正则表达式或计算一个角色的出现次数也不会拯救你。
最坏情况
test1,2|test3,4|test5
通过查看文本,可以使用|
作为分隔符来标记它。但,
和|
的出现频率是相同的。因此,从算法的角度来看,两种结果都是准确的:
更正结果
test1,2
test3,4
test5
错误的结果
test1
2|test3
4|test5
如果您提出了一套指南,或者您可以以某种方式控制CSV文件的生成,那么您可以尝试使用上述字符列表找到与String.contains()
方法一起使用的分隔符。例如:
public class MyClass {
private List<String> delimiterList = new ArrayList<>(){{
add(",");
add(";");
add("\t");
// etc...
}};
private static String determineDelimiter(String text) {
for (String delimiter : delimiterList) {
if(text.contains(delimiter)) {
return delimiter;
}
}
return "";
}
public static void main(String[] args) {
String csvFile = "/Users/csv/country.csv";
String line = "";
String cvsSplitBy = ",";
String delimiter = "";
boolean firstLine = true;
try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) {
while ((line = br.readLine()) != null) {
if(firstLine) {
delimiter = determineDelimiter(line);
if(delimiter.equalsIgnoreCase("")) {
System.out.println("Unsupported delimiter found: " + delimiter);
return;
}
firstLine = false;
}
// use comma as separator
String[] country = line.split(delimiter);
System.out.println("Country [code= " + country[4] + " , name=" + country[5] + "]");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
<强>更新强>
对于更优化的方式,在determineDelimiter()
方法而不是for-each
循环中,您可以使用正则表达式。
答案 2 :(得分:0)
如果分隔符可以出现在数据列中,那么您要求的是不可能的。例如,考虑CSV文件的第一行:
one,two:three
这可以是逗号分隔文件,也可以是冒号分隔文件。你不知道它是哪种类型。
如果您可以保证第一行的所有列都用引号括起来,例如,如果它始终是这种格式:
"one","two","three"
然后你可以使用这个逻辑(虽然它不是100%防弹):
if (line.contains("\",\""))
delimiter = ',';
else if (line.contains("\";\""))
delimiter = ';';
如果您不能保证这样的受限格式,那么最好将分隔符作为参数传递。
然后,您可以使用广为人知的开源CSV解析器(例如Apache Commons CSV)来阅读该文件。
答案 3 :(得分:0)
虽然我同意 Lefteris008 的观点,即不可能拥有正确确定所有情况的函数,但我们可以拥有一个既高效又在实践中给出大部分正确结果的函数。
def head(filename: str, n: int):
try:
with open(filename) as f:
head_lines = [next(f).rstrip() for x in range(n)]
except StopIteration:
with open(filename) as f:
head_lines = f.read().splitlines()
return head_lines
def detect_delimiter(filename: str, n=2):
sample_lines = head(filename, n)
common_delimiters= [',',';','\t',' ','|',':']
for d in common_delimiters:
ref = sample_lines[0].count(d)
if ref > 0:
if all([ ref == sample_lines[i].count(d) for i in range(1,n)]):
return d
return ','
我的高效实施基于
答案 4 :(得分:-1)
这取决于......
如果您的数据集总是长度相同和/或数据列中的分隔符永远不会出现,您可以只读取文件的第一行,查看渴望分隔符的内容,设置它然后读取其余部分使用该分隔符的文件。
像
这样的东西String csvFile = "/Users/csv/country.csv";
String line = "";
String cvsSplitBy = ",";
try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) {
while ((line = br.readLine()) != null) {
// use comma as separator
if (line.contains(",")) {
cvsSplitBy = ",";
} else if (line.contains(";")) {
cvsSplitBy = ";";
} else {
System.out.println("Wrong separator!");
}
String[] country = line.split(cvsSplitBy);
System.out.println("Country [code= " + country[4] + " , name=" + country[5] + "]");
}
} catch (IOException e) {
e.printStackTrace();
}
Greetz Kai
答案 5 :(得分:-1)
添加这样的条件,
String [] country;
if(line.contains(",")
country = line.split(",");
else if(line.contains(";"))
country=line.split(";");