C# 非常大的半csv文件-内存不足

C# 非常大的半csv文件-内存不足,c#,csv,out-of-memory,C#,Csv,Out Of Memory,我有一个2.6GB的半csv文件需要解析。我所说的半csv是指它的形式(数据,数据2,数据3,…)(更多数据,更多数据2,更多数据3,…)(…)。这意味着新行由“,”组成,而不是换行(这意味着整个文件基本上是一行) 我的计划是读入该文件并按“,”分开,然后我可以根据需要解析每个元素。显然,C#有“内存不足”的问题,但我不能只拆分文件,因为我不能保证拆分不会错误地拆分数据。有什么办法吗?完全未经测试,字符串必须是“mystring”。不支持字符串中的转义。不支持字符串中的“转义。因此这些无效:“我

我有一个2.6GB的半csv文件需要解析。我所说的半csv是指它的形式(数据,数据2,数据3,…)(更多数据,更多数据2,更多数据3,…)(…)。这意味着新行由“,”组成,而不是换行(这意味着整个文件基本上是一行)


我的计划是读入该文件并按“,”分开,然后我可以根据需要解析每个元素。显然,C#有“内存不足”的问题,但我不能只拆分文件,因为我不能保证拆分不会错误地拆分数据。有什么办法吗?

完全未经测试,字符串必须是
“mystring”
。不支持字符串中的转义。不支持字符串中的
转义。因此这些无效:
“我的”“引号”
“我的”“引号”
。文件必须是完美的:结尾没有eof,结尾没有新行,字符串内没有空格,字符串内没有新行。字符串内除了
(标记字符串结尾的)之外没有任何内容,没有元素太多的行,没有元素太少的行,没有
null
处理(从技术上讲,字符串的
将返回一个空字符串,不会引发错误)。支持
转换.ChangeType
支持的所有类型

用法:

using (var fs = new StreamReader("myfile.txt"))
{
    foreach (var objs in ParseStream(sr, new Type[] { typeof(int), typeof(double), typeof(string) }, CultureInfo.InvariantCulture))
    {
        // objs is an object[] where each member is of the type asked 
        // when ParseStream was called
    }
}
代码

公共静态IEnumerable ParseStream(TextReader tr,类型[]类型,IFormatProvider区域性=null) { var parts=新列表(); var sb=新的StringBuilder(); State State=State.WaitingForOpenBracket; 长列=-1; 长行=0; int-read; 而((read=tr.read())!=-1) { col++; char ch=(char)read; 如果(ch='\n') { col=0; 行++; } 其他的 { col++; } 开关(状态) { 案例状态。等待打开括号: 如果(ch!='(') { 抛出新异常(string.Format(“R:{0},C:{1},char:{2},row,col,ch的行的开头格式错误”); } state=state.WaitingForData; 打破 case State.WaitingForData: 案例状态.WaitingForColumnSeptor: 如果(ch==','| | ch==')') { 添加(sb.ToString()); (某人清楚地); 如果(parts.Count>types.Length) { 抛出新异常(string.Format(“从R:{0},C:{1}”,行,列开始的部分太多”); } 如果(ch==')') { var parts2=parts.Select((p,ix)=>Convert.ChangeType(p,types[ix],culture??CultureInfo.InvariantCulture)).ToArray(); 零件。清除(); 屈服返回部分2; state=state.WaitingForRowSeparator; } } 其他的 { if(state==state.waitingforcolumnseptor) { 抛出新异常(string.Format(“R:{0},C:{1},char:{2},row,col,ch处格式错误的列分隔符”); } 如果(ch==“”) { 如果(某人长度!=0) { 抛出新异常(string.Format(“R:{0},C:{1},char:{2},row,col,ch处格式错误的字符串”); } state=state.WaitingForEndQuotes; } 其他的 { 某人附加(ch); } } 打破 案例状态。WaitingForEndQuotes: 如果(ch==“”) { state=state.waitingforcolumnseptor; } 其他的 { 某人附加(ch); } 打破 案例状态。WaitingForRowSeparator: 如果(ch!=',') { 抛出新异常(string.Format(“R:{0},C:{1},char:{2},row,col,ch处格式错误的行分隔符”); } state=state.WaitingForOpenBracket; 打破 } } if(state!=state.WaitingForRowSeparator) { 抛出新的异常(string.Format(“R:{0},C:{1},row,col处文件的格式错误的结尾”); } }
仅为数字数据,或者可以包含字符串?您需要使用System.IO.BinaryReader。不要“读取”文件"。这就是问题所在。流式处理文件并逐行处理。这也会更快。您无法读取整个文件…由于编码,它将变为5gb…并且您的字符串长度不能超过2gb…然后将有一个
StringBuilder
来构建该字符串。内存太多。现在…如果没有字符串和文件的格式是完美的(在
)之间没有空格。这个问题相当复杂(我会说需要2/3个小时)…字符串的处理是+1h,带引号转义的字符串的处理是+1h,错误格式的处理是+1h…这个问题可能相当困难。
public static IEnumerable<object[]> ParseStream(TextReader tr, Type[] types, IFormatProvider culture = null)
{
    var parts = new List<string>();
    var sb = new StringBuilder();

    State state = State.WaitingForOpenBracket;

    long col = -1;
    long row = 0;

    int read;

    while ((read = tr.Read()) != -1)
    {
        col++;

        char ch = (char)read;

        if (ch == '\n')
        {
            col = 0;
            row++;
        }
        else
        {
            col++;
        }

        switch (state)
        {
            case State.WaitingForOpenBracket:
                if (ch != '(')
                {
                    throw new Exception(string.Format("Malformed begin-of-the-row at R: {0}, C: {1}, char: {2}", row, col, ch));
                }

                state = State.WaitingForData;

                break;

            case State.WaitingForData:
            case State.WaitingForColumnSeparator:
                if (ch == ',' || ch == ')')
                {
                    parts.Add(sb.ToString());

                    sb.Clear();

                    if (parts.Count > types.Length)
                    {
                        throw new Exception(string.Format("Too many parts starting at R: {0}, C: {1}", row, col));
                    }

                    if (ch == ')')
                    {
                        var parts2 = parts.Select((p, ix) => Convert.ChangeType(p, types[ix], culture ?? CultureInfo.InvariantCulture)).ToArray();
                        parts.Clear();                                

                        yield return parts2;

                        state = State.WaitingForRowSeparator;
                    }
                }
                else
                {
                    if (state == State.WaitingForColumnSeparator)
                    {
                        throw new Exception(string.Format("Malformed column separator at R: {0}, C: {1}, char: {2}", row, col, ch));
                    }

                    if (ch == '"')
                    {
                        if (sb.Length != 0)
                        {
                            throw new Exception(string.Format("Malformed string at R: {0}, C: {1}, char: {2}", row, col, ch));
                        }

                        state = State.WaitingForEndQuotes;
                    }
                    else
                    {
                        sb.Append(ch);
                    }
                }

                break;

            case State.WaitingForEndQuotes:
                if (ch == '"')
                {
                    state = State.WaitingForColumnSeparator;
                }
                else
                {
                    sb.Append(ch);
                }

                break;

            case State.WaitingForRowSeparator:
                if (ch != ',')
                {
                    throw new Exception(string.Format("Malformed row separator at R: {0}, C: {1}, char: {2}", row, col, ch));
                }

                state = State.WaitingForOpenBracket;

                break;
        }
    }

    if (state != State.WaitingForRowSeparator)
    {
        throw new Exception(string.Format("Malformed end-of-file at R: {0}, C: {1}", row, col));
    }
}