Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有自定义换行符的Streamreader-性能优化 编辑:请参阅下面的我的解决方案。。。_C#_Streamreader - Fatal编程技术网

C# 具有自定义换行符的Streamreader-性能优化 编辑:请参阅下面的我的解决方案。。。

C# 具有自定义换行符的Streamreader-性能优化 编辑:请参阅下面的我的解决方案。。。,c#,streamreader,C#,Streamreader,我有以下问题需要解决: 我们从不同的来源接收文件(主要是地址信息),这些文件可以在Windows标准中使用CR/LF('\r'\n')作为换行符,也可以在UNIX中使用LF('\n') 当使用StreamReader.ReadLine()方法在中读取文本时,这没有问题,因为它会平等地处理这两种情况 当文件中的某个地方有CR或LF而不应该存在时,就会出现问题。 例如,如果将包含单元格内换行符的EXCEL文件导出为.CSV或其他平面文件,则会发生这种情况 现在您有了一个具有以下结构的文件: Firs

我有以下问题需要解决: 我们从不同的来源接收文件(主要是地址信息),这些文件可以在Windows标准中使用CR/LF('\r'\n')作为换行符,也可以在UNIX中使用LF('\n')

当使用StreamReader.ReadLine()方法在中读取文本时,这没有问题,因为它会平等地处理这两种情况

当文件中的某个地方有CR或LF而不应该存在时,就会出现问题。 例如,如果将包含单元格内换行符的EXCEL文件导出为.CSV或其他平面文件,则会发生这种情况

现在您有了一个具有以下结构的文件:

FirstName;LastName;Street;HouseNumber;PostalCode;City;Country'\r''\n'
Jane;Doe;co James Doe'\n'TestStreet;5;TestCity;TestCountry'\r''\n'
John;Hancock;Teststreet;1;4586;TestCity;TestCounty'\r''\n'
现在,StreamReader.ReadLine()方法将第一行读取为:

FirstName;LastName;Street;HouseNumber;PostalCode;City;Country
这很好,但第二行是:

Jane;Doe;co James Doe
TestStreet;5;TestCity;TestCountry
这将破坏您的代码,或者您将得到错误的结果,如下所示:

Jane;Doe;co James Doe
TestStreet;5;TestCity;TestCountry
因此,我们通常通过一个工具来运行该文件,该工具检查周围是否有松散的“\n”或“\r”,并将其删除

但这一步很容易忘记,因此我尝试实现自己的ReadLine()方法。要求是它能够使用一个或两个换行符,并且这些字符可以由消费逻辑自由定义

这是我想到的课程:

 public class ReadFile
{
    private FileStream file;
    private StreamReader reader;

    private string fileLocation;
    private Encoding fileEncoding;
    private char lineBreak1;
    private char lineBreak2;
    private bool useSeccondLineBreak;

    private bool streamCreated = false;

    private bool endOfStream;

    public bool EndOfStream
    {
        get { return endOfStream; }
        set { endOfStream = value; }
    }

    public ReadFile(string FileLocation, Encoding FileEncoding, char LineBreak1, char LineBreak2, bool UseSeccondLineBreak)
    {
        fileLocation = FileLocation;
        fileEncoding = FileEncoding;
        lineBreak1 = LineBreak1;
        lineBreak2 = LineBreak2;
        useSeccondLineBreak = UseSeccondLineBreak;
    }

    public string ReadLine()
    {
        if (streamCreated == false)
        {
            file = new FileStream(fileLocation, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            reader = new StreamReader(file, fileEncoding);

            streamCreated = true;
        }

        StringBuilder builder = new StringBuilder();
        char[] buffer = new char[1];
        char lastChar = new char();
        char currentChar = new char();

        bool first = true;
        while (reader.EndOfStream != true)
        {
            if (useSeccondLineBreak == true)
            {
                reader.Read(buffer, 0, 1);
                lastChar = currentChar;

                if (currentChar == lineBreak1 && buffer[0] == lineBreak2)
                {
                    break;
                }
                else
                {
                    currentChar = buffer[0];
                }

                if (first == false)
                {
                    builder.Append(lastChar);
                }
                else
                {
                    first = false;
                }
            }
            else
            {
                reader.Read(buffer, 0, 1);

                if (buffer[0] == lineBreak1)
                {
                    break;
                }
                else
                {
                    currentChar = buffer[0];
                }

                builder.Append(currentChar);
            }
        }

        if (reader.EndOfStream == true)
        {
            EndOfStream = true;
        }

        return builder.ToString();
    }

    public void Close()
    {
        if (streamCreated == true)
        {
            reader.Close();
            file.Close();
        }
    }
}
这段代码工作得很好,它完成了它应该做的事情,但是与原来的StreamReader.ReadLine()方法相比,它慢了约3倍。当我们处理大型行计数时,这种差异不仅会被测量,而且会反映在现实世界的性能中。 (对于700000行,读取所有行、提取块并将其写入新文件需要约5秒,使用我的方法,在我的系统上需要约15秒)

我尝试了使用更大缓冲区的不同方法,但到目前为止,我无法提高性能

我感兴趣的是: 有什么建议可以改进这段代码的性能,使其更接近StreamReader.ReadLine()的原始性能吗

解决方案: 现在需要约6秒(相比之下,使用默认的“StreamReader.ReadLine()”需要约5秒)才能完成700000行与上述代码相同的操作

谢谢吉姆·米谢尔为我指明了正确的方向

public class ReadFile
    {
        private FileStream file;
        private StreamReader reader;

        private string fileLocation;
        private Encoding fileEncoding;
        private char lineBreak1;
        private char lineBreak2;
        private bool useSeccondLineBreak;

        const int BufferSize = 8192;
        int bufferedCount;
        char[] rest = new char[BufferSize];
        int position = 0;

        char lastChar;
        bool useLastChar;

        private bool streamCreated = false;

        private bool endOfStream;

        public bool EndOfStream
        {
            get { return endOfStream; }
            set { endOfStream = value; }
        }

        public ReadFile(string FileLocation, Encoding FileEncoding, char LineBreak1, char LineBreak2, bool UseSeccondLineBreak)
        {
            fileLocation = FileLocation;
            fileEncoding = FileEncoding;
            lineBreak1 = LineBreak1;
            lineBreak2 = LineBreak2;
            useSeccondLineBreak = UseSeccondLineBreak;
        }
 
        private int readInBuffer()
        {
            return reader.Read(rest, 0, BufferSize);
        }

        public string ReadLine()
        {
            StringBuilder builder = new StringBuilder();
            bool lineFound = false;

            if (streamCreated == false)
            {
                file = new FileStream(fileLocation, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 8192);

                reader = new StreamReader(file, fileEncoding);

                streamCreated = true;

                bufferedCount = readInBuffer();
            }
            
            while (lineFound == false && EndOfStream != true)
            {
                if (position < bufferedCount)
                {
                    for (int i = position; i < BufferSize; i++)
                    {
                        if (useLastChar == true)
                        {
                        useLastChar = false;

                        if (rest[i] == lineBreak2)
                        {
                            count++;
                            position = i + 1;
                            lineFound = true;
                            break;
                        }
                        else
                        {
                            builder.Append(lastChar);
                        }
                        }

                        if (rest[i] == lineBreak1)
                        {
                            if (useSeccondLineBreak == true)
                            {
                                if (i + 1 <= BufferSize - 1)
                                {
                                    if (rest[i + 1] == lineBreak2)
                                    {
                                        position = i + 2;
                                        lineFound = true;
                                        break;
                                    }
                                    else
                                    {
                                        builder.Append(rest[i]);
                                    }
                                }
                                else
                                {
                                    useLastChar = true;
                                    lastChar = rest[i];
                                }
                            }
                            else
                            {
                                position = i + 1;
                                lineFound = true;
                                break;
                            }
                        }
                        else
                        {
                            builder.Append(rest[i]);
                        }

                        position = i + 1;
                    }
                    
                }
                else
                {
                    bufferedCount = readInBuffer();
                    position = 0;
                }
            }

            if (reader.EndOfStream == true && position == bufferedCount)
            {
                EndOfStream = true;
            }

            return builder.ToString();
        }


        public void Close()
        {
            if (streamCreated == true)
            {
                reader.Close();
                file.Close();
            }
        }
    }
公共类读取文件
{
私有文件流文件;
私有流阅读器;
私有字符串文件位置;
私有编码文件编码;
私有字符行break1;
私有字符行break2;
私人住宅使用条件中断;
const int BufferSize=8192;
int缓冲计数;
char[]rest=新字符[BufferSize];
int位置=0;
char lastChar;
布尔·乌斯拉斯卡尔;
private bool streamCreated=false;
私有bool内流;
公共布尔内流
{
获取{return endOfStream;}
设置{endOfStream=value;}
}
public ReadFile(字符串文件位置、编码文件编码、char LineBreak1、char LineBreak2、bool usecondlinebreak)
{
fileLocation=fileLocation;
fileEncoding=fileEncoding;
lineBreak1=lineBreak1;
lineBreak2=lineBreak2;
UseSeCondlineBreak=UseSeCondlineBreak;
}
私有int readInBuffer()
{
返回reader.Read(rest,0,BufferSize);
}
公共字符串读取行()
{
StringBuilder=新的StringBuilder();
bool lineFound=false;
if(streamCreated==false)
{
file=newfilestream(fileLocation,FileMode.Open,FileAccess.Read,FileShare.ReadWrite,8192);
reader=新的StreamReader(文件、文件编码);
streamCreated=true;
bufferedCount=readInBuffer();
}
while(lineFound==false&&EndOfStream!=true)
{
如果(位置<缓冲计数)
{
for(int i=位置;i如果(i+1,加速的方法是让它一次读取多个字符。例如,创建一个4 KB的缓冲区,将数据读入该缓冲区,然后逐个字符进行。如果您将一个字符一个字符复制到
StringBuilder
,这非常简单

下面的代码显示了如何解析循环中的行。您必须将其拆分,以便它可以维护调用之间的状态,但这应该会给您一个想法

const int BufferSize = 4096;
const string newline = "\r\n";

using (var strm = new StreamReader(....))
{
    int newlineIndex = 0;
    var buffer = new char[BufferSize];
    StringBuilder sb = new StringBuilder();
    int charsInBuffer = 0;
    int bufferIndex = 0;
    char lastChar = (char)-1;

    while (!(strm.EndOfStream && bufferIndex >= charsInBuffer))
    {
        if (bufferIndex > charsInBuffer)
        {
            charsInBuffer = strm.Read(buffer, 0, buffer.Length);
            if (charsInBuffer == 0)
            {
                // nothing read. Must be at end of stream.
                break;
            }
            bufferIndex = 0;
        }
        if (buffer[bufferIndex] == newline[newlineIndex])
        {
            ++newlineIndex;
            if (newlineIndex == newline.Length)
            {
                // found a line
                Console.WriteLine(sb.ToString());
                newlineIndex = 0;
                sb = new StringBuilder();
            }
        }
        else
        {
            if (newlineIndex > 0)
            {
                // copy matched newline characters
                sb.Append(newline.Substring(0, newlineIndex));
                newlineIndex = 0;
            }
            sb.Append(buffer[bufferIndex]);
        }
        ++bufferIndex;
    }
    // Might be a line left, without a newline
    if (newlineIndex > 0)
    {
        sb.Append(newline.Substring(0, newlineIndex));
    }
    if (sb.Length > 0)
    {
        Console.WriteLine(sb.ToString());
    }
}
您可以通过跟踪起始位置来对此进行优化,这样当您找到一行时,就可以创建一个从
buffer[start]
buffer[current]的字符串
,而不创建
StringBuilder
。而是调用构造函数。当您跨越缓冲区边界时,这有点棘手。在这种情况下,可能希望将跨越缓冲区边界作为特例处理,并使用
StringBuilder
作为临时存储


不过,在第一个版本开始运行之前,我不会为这种优化而烦恼。

这不是我想要的实现