Java中通过逗号或双引号对拆分字符串的优雅算法
问题很简单 CSV文件如下所示:Java中通过逗号或双引号对拆分字符串的优雅算法,java,algorithm,Java,Algorithm,问题很简单 CSV文件如下所示: 1, "John", "John Joy" 1, "John", "Joy, John" 如果我想得到每一列,我只需要使用String[]splits=line.split 如果CSV文件如下所示: 1, "John", "John Joy" 1, "John", "Joy, John" 所以在双引号对中有一个逗号。上面的拆分将不再有效,因为我想要Joy,John作为一个完整的部分 那么,是否有一种优雅/简单的算法来处理这种情况 编辑: 请不要认为这是一
1, "John", "John Joy"
1, "John", "Joy, John"
如果我想得到每一列,我只需要使用String[]splits=line.split
如果CSV文件如下所示:
1, "John", "John Joy"
1, "John", "Joy, John"
所以在双引号对中有一个逗号。上面的拆分将不再有效,因为我想要Joy,John作为一个完整的部分
那么,是否有一种优雅/简单的算法来处理这种情况
编辑:
请不要认为这是一个正式的CSV解析的事情。我只是将CSV用作需要拆分的用例
我真正想要的不是一个合适的CSV解析器,相反,我只想要一个算法,它可以在考虑双引号的情况下用逗号正确地分割一行。如果不这样做是为了学习,最好使用现有库,而不是编写自定义实现。 因为CSV有一些您在自定义实现中可能会错过的细节,而且通常库是经过良好测试的 在这里你可以找到一些好的 编辑 我创建了一个方法来解析您的字符串,但由于我没有对它进行很好的测试,它的工作可能并不完美。 这可能只是你的一个起点,你可以进一步改进它
String inputString = "1, \"John\",\"Joy, John\"";
char quote = '"';
List<String> csvList = new ArrayList<String>();
boolean inQuote = false;
int lastStart = 0;
for (int i = 0; i < inputString.length(); i++) {
if ((i + 1) == inputString.length()) {
//if this is the last character
csvList.add(inputString.substring(lastStart, i + 1));
}
if (inputString.charAt(i) == quote) {
//if the character is quote
if (inQuote) {
inQuote = false;
continue; //escape
}
inQuote = true;
continue;
}
if (inputString.charAt(i) == ',') {
if (inQuote) continue;
csvList.add(inputString.substring(lastStart, i));
lastStart = i + 1;
}
}
System.out.println(csvList);
有问题吗
如果你能得到像这样的线,约翰,乔伊,约翰
Joy,John?如果不是为了学习,最好使用现有库,而不是编写自定义实现。 因为CSV有一些您在自定义实现中可能会错过的细节,而且通常库是经过良好测试的 在这里你可以找到一些好的 编辑 我创建了一个方法来解析您的字符串,但由于我没有对它进行很好的测试,它的工作可能并不完美。 这可能只是你的一个起点,你可以进一步改进它
String inputString = "1, \"John\",\"Joy, John\"";
char quote = '"';
List<String> csvList = new ArrayList<String>();
boolean inQuote = false;
int lastStart = 0;
for (int i = 0; i < inputString.length(); i++) {
if ((i + 1) == inputString.length()) {
//if this is the last character
csvList.add(inputString.substring(lastStart, i + 1));
}
if (inputString.charAt(i) == quote) {
//if the character is quote
if (inQuote) {
inQuote = false;
continue; //escape
}
inQuote = true;
continue;
}
if (inputString.charAt(i) == ',') {
if (inQuote) continue;
csvList.add(inputString.substring(lastStart, i));
lastStart = i + 1;
}
}
System.out.println(csvList);
有问题吗
如果你能得到像这样的线,约翰,乔伊,约翰
Joy,John?中的两个引号使用正则表达式非常优雅。 抱歉,我不熟悉Java正则表达式,所以我的示例是Lua: 这个例子没有考虑到在引用的文本中可能有换行符,而在引用的文本中原始的引号字符会加倍
--- file.csv
1, "John", "John Joy"
2, "John", "Joy, John"
--- Lua code
for line in io.lines 'file.csv' do
print '==='
for _, s in (line..','):gmatch '%s*("?)(.-)%1%s*,' do
print(s)
end
end
--- Output
===
1
John
John Joy
===
2
John
Joy, John
使用正则表达式非常优雅。 抱歉,我不熟悉Java正则表达式,所以我的示例是Lua: 这个例子没有考虑到在引用的文本中可能有换行符,而在引用的文本中原始的引号字符会加倍
--- file.csv
1, "John", "John Joy"
2, "John", "Joy, John"
--- Lua code
for line in io.lines 'file.csv' do
print '==='
for _, s in (line..','):gmatch '%s*("?)(.-)%1%s*,' do
print(s)
end
end
--- Output
===
1
John
John Joy
===
2
John
Joy, John
可以从正则表达式开始:
[^",]*|"[^"]*"
它匹配不包含逗号的非引号字符串或引号字符串。然而,还有很多问题,包括:
你的输入中逗号后面真的有空格吗?或者,更一般地说,您是否允许不完全位于字段第一个字符处的引号
如何在包含引号的字段周围加引号
根据你如何回答这个问题,你可能会得到不同的正则表达式。事实上,使用CSV解析库的常规建议与处理角落案例无关;这是关于不必考虑它们,因为您假定标准的CSV处理,不管解析库的作者认为这可能是什么。真是一团糟
我成功地使用了一个正则表达式,尽管它与CSV不兼容:
(?:[^",]|"[^"]*")*
这与第一个非常相似,只是它允许任何数量的串联字段,因此以下两个字段都可以识别为单个字段:
"John"", Mary"
John", "Mary
CSV标准将第一个视为代表:
John", Mary -- internal quote
并将第二个字段中的引号视为普通字符,从而生成两个字段。所以YMMV
在任何情况下,一旦您决定一个合适的正则表达式,算法都很简单。在伪代码中,因为我远不是Java专家
repeat:
match the regex at the current position
and append the result to the result;
if the match fails:
report error
if the match goes to the end of the string:
done
if the next character is a ',':
advance the position by one
otherwise:
report error
根据正则表达式的不同,报告错误的两种情况可能是不可能的。通常,如果引用的字段未终止,并且您需要决定是否允许在引用的字段中添加新行,则会触发第一个。如果使用了我提供的第一个正则表达式,但没有立即在带引号的字符串后面加逗号,则可能会出现第二种情况。可以从正则表达式开始:
[^",]*|"[^"]*"
// use regxep with matcher
String string1 = "\"John\", \"John Joy\"";
String string2 = "\"John\", \"Joy, John\"";
Pattern pattern = Pattern.compile("\"[^\"]+\"");
Matcher matcher = pattern.matcher(string1);
System.out.println("string1: " + string1);
int start = 0;
while(matcher.find(start)){
System.out.println(matcher.group());
start = matcher.end() + 1;
if(start > string1.length())
break;
}
matcher = pattern.matcher(string2);
System.out.println("string2: " + string2);
start = 0;
while(matcher.find(start)){
System.out.println(matcher.group());
start = matcher.end() + 1;
if(start > string2.length())
break;
}
它匹配不包含逗号的非引号字符串或引号字符串。然而,还有很多问题,包括:
你的输入中逗号后面真的有空格吗?或者,更一般地说,您是否允许不完全位于字段第一个字符处的引号
如何在包含引号的字段周围加引号
根据你如何回答这个问题,你可能会得到不同的正则表达式。事实上,使用CSV解析库的常规建议与处理角落案例无关;这是关于不必考虑他们,因为你
我使用标准的CSV处理,不管根据解析库的作者是什么。真是一团糟
我成功地使用了一个正则表达式,尽管它与CSV不兼容:
(?:[^",]|"[^"]*")*
这与第一个非常相似,只是它允许任何数量的串联字段,因此以下两个字段都可以识别为单个字段:
"John"", Mary"
John", "Mary
CSV标准将第一个视为代表:
John", Mary -- internal quote
并将第二个字段中的引号视为普通字符,从而生成两个字段。所以YMMV
在任何情况下,一旦您决定一个合适的正则表达式,算法都很简单。在伪代码中,因为我远不是Java专家
repeat:
match the regex at the current position
and append the result to the result;
if the match fails:
report error
if the match goes to the end of the string:
done
if the next character is a ',':
advance the position by one
otherwise:
report error
根据正则表达式的不同,报告错误的两种情况可能是不可能的。通常,如果引用的字段未终止,并且您需要决定是否允许在引用的字段中添加新行,则会触发第一个。如果使用了我提供的第一个正则表达式,但没有立即在带引号的字符串后面加逗号,则可能会出现第二种情况。首先在引号中拆分字符串。奇数段将引用内容;即使是一个,也必须用逗号再拆分一次。我在日志上使用它,引用的文本没有转义引号,就像这个问题一样
// use regxep with matcher
String string1 = "\"John\", \"John Joy\"";
String string2 = "\"John\", \"Joy, John\"";
Pattern pattern = Pattern.compile("\"[^\"]+\"");
Matcher matcher = pattern.matcher(string1);
System.out.println("string1: " + string1);
int start = 0;
while(matcher.find(start)){
System.out.println(matcher.group());
start = matcher.end() + 1;
if(start > string1.length())
break;
}
matcher = pattern.matcher(string2);
System.out.println("string2: " + string2);
start = 0;
while(matcher.find(start)){
System.out.println(matcher.group());
start = matcher.end() + 1;
if(start > string2.length())
break;
}
boolean quoted = false;
for(String q : str.split("\"")) {
if(quoted)
System.out.println(q.trim());
else
for(String s : q.split(","))
if(!s.trim().isEmpty())
System.out.println(s.trim());
quoted = !quoted;
}
首先在引号上拆分字符串。奇数段将引用内容;即使是一个,也必须用逗号再拆分一次。我在日志上使用它,引用的文本没有转义引号,就像这个问题一样
boolean quoted = false;
for(String q : str.split("\"")) {
if(quoted)
System.out.println(q.trim());
else
for(String s : q.split(","))
if(!s.trim().isEmpty())
System.out.println(s.trim());
quoted = !quoted;
}
CSV文件如下所示:一个简单的文件就是这样。复杂的字段可以在引用的字段中有换行符。因此,如果您正在逐行阅读它,请注意,除非您的行感知代码正在处理引号内的换行符,否则您的行变量只能包含记录的一部分。您需要一个CSV解析器,一个简单的状态机就可以了。如果您所说的优雅是指类似于单个正则表达式的东西:没有。CSV比看上去复杂得多:多行字段、转义引号等等。不过,您可以使用CSV解析器库:例如OpenCSV,但肯定也有一个Apache解析器库。如何处理上述两种情况?CSV文件如下所示:一个简单的文件就可以。复杂的字段可以在引用的字段中有换行符。因此,如果您正在逐行阅读它,请注意,除非您的行感知代码正在处理引号内的换行符,否则您的行变量只能包含记录的一部分。您需要一个CSV解析器,一个简单的状态机就可以了。如果您所说的优雅是指类似于单个正则表达式的东西:没有。CSV比看上去复杂得多:多行字段、转义引号等等。不过,您可以使用CSV解析器库:例如OpenCSV,但肯定也有一个Apache解析器库。只处理上述两种情况如何?只处理上述两种情况如何?@Jackson Tale-Joy中的两个引号,John不适合CSV。但最初的引号会加倍。例如,John说:OK将成为John说:CSV中的OK。就像这样:1,John,Joy,John如何处理上述两个案例?@Jackson Tale-Joy中的两个引号,John不适合CSV。但最初的引号会加倍。例如,John说:OK将成为John说:CSV中的OK。就像这样:1,John,Joy,如果你能解释你的答案,比如为什么你认为它很优雅或者它所采用的方法等等,那就太好了。亚历克斯:如果你能解释你的答案,比如为什么你认为它很优雅或者它所采用的方法等等,那就太好了。解释一下你的答案。只是不喜欢正则表达式。上面的代码应该按要求拆分str。我认为@Kumar的意思是,仅代码的答案通常是不可接受的,例如,解释一下你的答案。只是不喜欢正则表达式。上面的代码应该按要求拆分str。我认为@Kumar的意思是,仅代码的答案通常是不可接受的,例如。