【发布时间】:2014-07-19 10:00:43
【问题描述】:
我想读取一个包含逗号分隔值的文件,所以写了一个有限状态机:
private IList<string> Split(string line)
{
List<string> values = new List<string>();
string value = string.Empty;
ParseState state = ParseState.Initial;
foreach (char c in line)
{
switch (state)
{
case ParseState.Initial:
switch (c)
{
case COMMA:
values.Add(string.Empty);
break;
case QUOTE:
state = ParseState.Quote;
break;
default:
value += c;
state = ParseState.Data;
break;
}
break;
case ParseState.Data:
switch (c)
{
case COMMA:
values.Add(value);
value = string.Empty;
state = ParseState.Initial;
break;
case QUOTE:
throw new InvalidDataException("Improper quotes");
default:
value += c;
break;
}
break;
case ParseState.Quote:
switch (c)
{
case QUOTE:
state = ParseState.QuoteInQuote;
break;
default:
value += c;
break;
}
break;
case ParseState.QuoteInQuote:
switch (c)
{
case COMMA:
values.Add(value);
value = string.Empty;
state = ParseState.Initial;
break;
case QUOTE:
value += c;
state = ParseState.Quote;
break;
default:
throw new InvalidDataException("Unpaired quotes");
}
break;
}
}
switch (state)
{
case ParseState.Initial:
case ParseState.Data:
case ParseState.QuoteInQuote:
values.Add(value);
break;
case ParseState.Quote:
throw new InvalidDataException("Unclosed quotes");
}
return values;
}
是的,我知道关于 CSV 解析器的建议是“不要自己编写”,但是
- 我很快就需要它,并且
- 我们工作中的下载政策需要几天时间才能让我 从网络上获取开源。
嘿,至少我没有以string.Split() 开头,或者更糟糕的是,尝试使用Regex!
是的,我知道可以通过使用StringBuilder 来改进它,而且它对数据中的引号有限制,但是
- 性能不是问题,并且
- 这只是为了在内部生成定义明确的测试数据,
所以我不在乎那些。
我做关心的是末尾明显的尾随块,用于在最后一个逗号之后清除所有数据,以及它开始看起来像某种反模式的方式在那里,这正是 FSM 等“好”模式应该避免的那种事情。
所以我的问题是:最后的这个块是某种反模式吗,它是不是将来会回来咬我?
【问题讨论】:
-
您尝试过.net 框架附带的TextFieldParser 吗?我不确定这会对你有帮助,我没用过。
-
这可能属于 codereview.se
-
他们是否能够在不对整个方法进行全面审查的情况下将答案限制在我的问题上?
-
只是为了碰撞@SriramSakthivel:TextFieldParser 是一个非常好的、简单的解决方案。是的,您需要添加对属于 .Net 的 VB 程序集的引用,这感觉不自然但完全没问题;我有一个使用这种方法的大型生产应用程序。如果您在工作中设法绕过下载限制,CsvHelper (github.com/JoshClose/CsvHelper) 非常棒 - 它现在是我们处理任何与 CSV 相关的标准工具。
-
@Frans CsvHelper 本来是我的首选,而且我确实已经向技术服务部门提出了让我下载它的请求,但是程序就是程序……