我有一个很大的字符串(我称它为CSV文件,尽管实际上不是一个,但现在会更简单),我必须在C#代码中进行解析。
解析过程的第一步是通过仅使用StreamReader对象并调用ReadLine直到文件通过,将文件分成几行。 但是,任何给定的行都可能包含带引号(单引号)的文字和嵌入的换行符。 我需要找到这些换行符,并将它们临时转换为其他类型的令牌或转义序列,直到将文件拆分成行数组为止。然后可以将其改回。
输入数据示例:
1 2 3
| 1,2,10,99,'Some text without a newline', true, false, 90
2,1,11,98,'This text has an embedded newline
and continues here', true, true, 90 |
我可以使用string.IndexOf来找到引用的部分并在其中查找换行符,从而编写完成此操作所需的所有C#代码,但我认为正则表达式可能是一个更好的选择(即,现在我有两个问题)
使用C#2.0迭代器使完成此类工作的状态机变得容易。希望这是我将要编写的最后一个CSV解析器。整个文件被视为一串可枚举的字符串,即行/列。 IEnumerable很棒,因为它可以由LINQ运算符处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| public class CsvParser
{
public char FieldDelimiter { get; set; }
public CsvParser()
: this(',')
{
}
public CsvParser(char fieldDelimiter)
{
FieldDelimiter = fieldDelimiter;
}
public IEnumerable<IEnumerable<string>> Parse(string text)
{
return Parse(new StringReader(text));
}
public IEnumerable<IEnumerable<string>> Parse(TextReader reader)
{
while (reader.Peek() != -1)
yield return parseLine(reader);
}
IEnumerable<string> parseLine(TextReader reader)
{
bool insideQuotes = false;
StringBuilder item = new StringBuilder();
while (reader.Peek() != -1)
{
char ch = (char)reader.Read();
char? nextCh = reader.Peek() > -1 ? (char)reader.Peek() : (char?)null;
if (!insideQuotes && ch == FieldDelimiter)
{
yield return item.ToString();
item.Length = 0;
}
else if (!insideQuotes && ch == '\
' && nextCh == '\
') //CRLF
{
reader.Read(); // skip LF
break;
}
else if (!insideQuotes && ch == '\
') //LF for *nix-style line endings
break;
else if (ch == '"' && nextCh == '"') // escaped quotes""
{
item.Append('"');
reader.Read(); // skip next"
}
else if (ch == '"')
insideQuotes = !insideQuotes;
else
item.Append(ch);
}
// last one
yield return item.ToString();
}
} |
请注意,逐字符读取文件,其代码决定何时将换行符视为行定界符或带引号的字符串的一部分。
由于这不是真正的CSV文件,因此它具有任何形式的架构吗?
从您的示例中,您看起来像:
int,int,int,int,string,bool,bool,int
这样就构成了您的记录/对象。
假设您的数据格式正确(我对您的消息来源了解不足,不足以知道此假设的有效性);你可以:
阅读您的台词。
使用状态机来解析您的数据。
如果您的行结束,并且您正在解析字符串,请阅读下一行并继续进行解析。
如果可能,我会避免使用正则表达式。
如果将整个文件放入一个变量,然后根据未引用的换行符将其拆分怎么办?
编辑:对不起,我误解了您的帖子。如果您正在寻找正则表达式,那么这里是一个:
1 2
| content = Regex.Replace(content,"'([^']*)\
([^']*)'","'\\1TOKEN\\2'"); |
可能存在一些极端情况,并且存在两个问题,但我认为大多数时候都可以。 Regex的作用是,它首先找到之间有\ n的任何一对单引号,然后用TOKEN替换该\ n并保留中间的任何文本。
但是仍然,我会像下面的@bryansh一样去状态机。