我有一个很大的字符串(让我们把它称为CSV文件,虽然它实际上不是一个,现在只是更容易),我必须用C#代码解析。
解析过程的第一步是通过使用StreamReader
对象并调用ReadLine
直到它通过文件将文件拆分为单独的行。但是,任何给定的行都可能包含带引号换行符的带引号(单引号)字面值。我需要找到这些换行符并将它们暂时转换为其他类型的令牌或转义序列,直到我将文件拆分为一个行数组。然后我可以将它们更改回来。
示例输入数据:
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
我可以编写所有需要执行此操作的C#代码,使用string.IndexOf
查找引用的部分并在其中查找换行符,但我认为正则表达式可能是更好的选择(即{{ 3}})
答案 0 :(得分:3)
由于这不是真正的CSV文件,它是否有任何类型的架构?
从您的示例中,您看起来像: int,int,int,int,string,bool,bool,int
用它来组成你的记录/对象。
假设您的数据格式正确(我对您的来源了解不足以了解此假设的有效性);你可以:
如果可能,我会避免使用正则表达式。
答案 1 :(得分:3)
使用C#2.0迭代器可以轻松完成执行此类工作的状态机。希望这是我写的最后一个CSV解析器。整个文件被视为可枚举的一串可枚举字符串,即行/列。 IEnumerable非常棒,因为它可以由LINQ运算符处理。
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 == '\r' && nextCh == '\n') //CRLF
{
reader.Read(); // skip LF
break;
}
else if (!insideQuotes && ch == '\n') //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();
}
}
请注意,文件是逐个字符读取的,代码决定何时将换行符视为行分隔符或引用字符串的一部分。
答案 2 :(得分:1)
如果您将整个文件放入变量然后根据非引用的换行符拆分该怎么办?
答案 3 :(得分:0)
编辑:抱歉,我误解了你的帖子。如果你正在寻找一个正则表达式,那么这里有一个:
content = Regex.Replace(content, "'([^']*)\n([^']*)'", "'\1TOKEN\2'");
可能存在边缘情况和两个问题,但我认为大部分时间都应该没问题。正则表达式的作用是它首先找到任何一对单引号,它们之间有\ n并用TOKEN替换\ n,保留其间的任何文本。
但是,我仍然会像@bryansh在下面解释的那样使用状态机。