C# 超大单行文件解析
我正在从一个站点下载数据,该站点以非常大的块向我提供数据。在非常大的块中,有一些“块”需要单独解析。这些“块”以“(ClinicalData)”开头,以“(/ClinicalData)”结尾。因此,示例字符串如下所示:C# 超大单行文件解析,c#,string,file-io,C#,String,File Io,我正在从一个站点下载数据,该站点以非常大的块向我提供数据。在非常大的块中,有一些“块”需要单独解析。这些“块”以“(ClinicalData)”开头,以“(/ClinicalData)”结尾。因此,示例字符串如下所示: (ClinicalData)(ID="1")(/ClinicalData)(ClinicalData)(ID="2")(/ClinicalData)(ClinicalData)(ID="3")(/ClinicalData)(ClinicalData)(ID="4")(/Clini
(ClinicalData)(ID="1")(/ClinicalData)(ClinicalData)(ID="2")(/ClinicalData)(ClinicalData)(ID="3")(/ClinicalData)(ClinicalData)(ID="4")(/ClinicalData)(ClinicalData)(ID="5")(/ClinicalData)
enum ReadState
{
Start,
SawOpen
}
using (var sr = new StreamReader(@"path\to\clinic.txt"))
using (var sw = new StreamWriter(@"path\to\output.txt"))
{
var rs = ReadState.Start;
while (true)
{
var r = sr.Read();
if (r < 0)
{
if (rs == ReadState.SawOpen)
sw.Write('<');
break;
}
char c = (char) r;
if ((c == '\r') || (c == '\n'))
continue;
if (rs == ReadState.SawOpen)
{
if (c == 'C')
sw.WriteLine();
sw.Write('<');
rs = ReadState.Start;
}
if (c == '<')
{
rs = ReadState.SawOpen;
continue;
}
sw.Write(c);
}
}
在“理想”情况下,块是指一行数据,但有时会出现错误的换行符。因为我想解析块中的(ClinicalData)块,所以我想让我的数据能够逐行解析。因此,我将文本文件读入StringBuilder,删除新行(以防万一),然后插入自己的新行,这样我就可以逐行读取
StringBuilder dataToWrite = new StringBuilder(File.ReadAllText(filepath), Int32.MaxValue);
// Need to clear newline characters just in case they exist.
dataToWrite.Replace("\n", "");
// set my own newline characters so the data becomes parse-able by line
dataToWrite.Replace("<ClinicalData", "\n<ClinicalData");
// set the data back into a file, which is then used in a StreamReader to parse by lines.
File.WriteAllText(filepath, dataToWrite.ToString());
StringBuilder-dataToWrite=new-StringBuilder(File.ReadAllText(filepath),Int32.MaxValue);
//需要清除换行符,以防它们存在。
dataToWrite.Replace(“\n”和“);
//设置我自己的换行符,以便数据可以按行解析
dataToWrite.Replace(“首先,我认为您不需要将所有文本放入StringBuilder中,因为您甚至不需要将部分连接到它。您可以尝试以下操作:
File.ReadAllText(filepath).Replace("\n", "").Replace("<ClinicalData", "\n<ClinicalData");
有很多方法可以实现类似的东西,但希望这能帮助您开始
StreamReader的ReadLine()方法只是从文件中读取文本的多种方法之一。您可以将文本读入指定长度的缓冲区,然后解析出ClinicalData标记。如果您愿意,我可以提供一个示例。
或者,如果您正在读取XML文件,XmlReader是另一个选项。
这是一种非常低效的读取文本文件的方法,更不用说大的文本文件了。如果您只需要一次传递、替换或添加单个字符,则应使用StreamReader
。如果您只需要一个前瞻字符,则只需保持一个中间状态,例如:
(ClinicalData)(ID="1")(/ClinicalData)(ClinicalData)(ID="2")(/ClinicalData)(ClinicalData)(ID="3")(/ClinicalData)(ClinicalData)(ID="4")(/ClinicalData)(ClinicalData)(ID="5")(/ClinicalData)
enum ReadState
{
Start,
SawOpen
}
using (var sr = new StreamReader(@"path\to\clinic.txt"))
using (var sw = new StreamWriter(@"path\to\output.txt"))
{
var rs = ReadState.Start;
while (true)
{
var r = sr.Read();
if (r < 0)
{
if (rs == ReadState.SawOpen)
sw.Write('<');
break;
}
char c = (char) r;
if ((c == '\r') || (c == '\n'))
continue;
if (rs == ReadState.SawOpen)
{
if (c == 'C')
sw.WriteLine();
sw.Write('<');
rs = ReadState.Start;
}
if (c == '<')
{
rs = ReadState.SawOpen;
continue;
}
sw.Write(c);
}
}
enum ReadState
{
开始
锯开
}
使用(var sr=new StreamReader(@“path\to\clinic.txt”))
使用(var sw=new StreamWriter(@“path\to\output.txt”))
{
var rs=ReadState.Start;
while(true)
{
var r=sr.Read();
if(r<0)
{
if(rs==ReadState.SawOpen)
sw.Write('您不是要求前面有一个2GB容量的StringBuilder吗?您会得到哪一行异常?您还可以在StringBuilder之前将File.ReadAllText(filepath)放在一个单独的语句中并进行测试吗?如果您在读取非常大的文件时遇到问题,请尝试使用“MemoryMappedFile”。看看它是否解决了这个问题'(ClinicalData)(ID=“1”)(/ClinicalData)”在您的示例中应该是“”?到500-内部服务器错误:我认为我也是,但这是一行失败的280MB文本。到Furkan Omay:在创建StringBuilder时发生异常。我不认为错误是由于File.ReadAllText造成的,因为我可以执行:String[]contents=File.ReadAllLines(filepath);一切都很好(我知道它们是不同的方法)。内容数组在我尝试访问它时就会失败。此外,由于所有数据都在一行中,因此内容的大小为1。对于用户2012384:MemoryMapping不起作用,因为您必须读取TechUnks,正如最初指定的那样,我失败了。回到办公室后,我将尝试您的一体化声明,谢谢Arin。我不使用StreamReader,因为在我选择的“块大小”切掉了我所需“块”的中间部分的情况下,我无法想出可以创建我所需块的逻辑。例如(不是一个确切的测试用例),使用我最初发布的块:Pass 1:“(ClinicalData)(ID=“1”)(/ClinicalData)(Clin“Pass2:”icalData)(ID=“2”)(/ClinicalDa“Pass 3:“ta”(ClinicalData)(ID=“3”)(/ClinicalData)(ClinicalData)(ID=“4”)(/Cl”Pass 4:“inicalData)(ClinicalData)(ID=“5”)(/ClinicalData)”。让您在Pass上停下来:我无法确定要分析的每个所需“块”的确切大小,因为某些块具有附加属性,而其他块如果留空则不会具有这些属性。例如:(ClinicalData)(ID=“1”)(Name=“Scooter”)(/ClinicalData)(ClinicalData)(ID=“2”)(/ClinicalData)。最后:我收到的数据不是真正的XML格式,因此结构无法解析。我熟悉XML解析和LINQ to XML,遗憾的是我不能使用它。是的,但假设您的第一块代码中有3个半ClientData标记。您可以解析这3个ClientData标记,然后将字符串的其余部分存储在内存中的某个位置y、 加载下一个块并将第一个块中剩余的字符串附加到开头并重复。对,我未能正确实现的部分是能够将字符串拆分为块,同时将剩余的字符串保留在内存中以便与下一个解析的块连接。感谢Dour High Arch的深入研究我会给你一次一个字符的方法,稍作修改。1)保留字符的集合,以便我可以跟踪已完成的块,从而允许我通过行解析为将来编写行。请注意,此代码无法处理CDATA部分中不匹配的
字符。处理这些字符需要额外的读取状态。
enum ReadState
{
Start,
SawOpen
}
using (var sr = new StreamReader(@"path\to\clinic.txt"))
using (var sw = new StreamWriter(@"path\to\output.txt"))
{
var rs = ReadState.Start;
while (true)
{
var r = sr.Read();
if (r < 0)
{
if (rs == ReadState.SawOpen)
sw.Write('<');
break;
}
char c = (char) r;
if ((c == '\r') || (c == '\n'))
continue;
if (rs == ReadState.SawOpen)
{
if (c == 'C')
sw.WriteLine();
sw.Write('<');
rs = ReadState.Start;
}
if (c == '<')
{
rs = ReadState.SawOpen;
continue;
}
sw.Write(c);
}
}