sourcetip

텍스트 파일을 한 줄씩 읽는 가장 빠른 방법은 무엇입니까?

fileupload 2023. 5. 3. 21:39
반응형

텍스트 파일을 한 줄씩 읽는 가장 빠른 방법은 무엇입니까?

텍스트 파일을 한 줄씩 읽고 싶습니다.제가 이 작업을 최대한 효율적으로 수행하고 있는지 알고 싶었습니다.NET C# 사물의 범위.

제가 지금까지 시도하고 있는 것은 다음과 같습니다.

var filestream = new System.IO.FileStream(textFilePath,
                                          System.IO.FileMode.Open,
                                          System.IO.FileAccess.Read,
                                          System.IO.FileShare.ReadWrite);
var file = new System.IO.StreamReader(filestream, System.Text.Encoding.UTF8, true, 128);

while ((lineOfText = file.ReadLine()) != null)
{
    //Do something with the lineOfText
}

파일을 한 줄씩 읽는 가장 빠른 방법을 찾으려면 벤치마킹을 몇 가지 수행해야 합니다.내 컴퓨터에서 몇 가지 작은 테스트를 수행했지만 내 결과가 당신의 환경에 적용될 것이라고 기대할 수 없습니다.

스트림 판독기 사용.선 읽기

이것은 기본적으로 당신의 방법입니다.어떤 이유로 버퍼 크기를 가능한 가장 작은 값(128)으로 설정합니다.이 값을 늘리면 일반적으로 성능이 향상됩니다.기본 크기는 1,024이고 기타 적합한 옵션은 512(Windows의 섹터 크기) 또는 4,096(NTFS의 클러스터 크기)입니다.최적의 버퍼 크기를 결정하려면 벤치마크를 실행해야 합니다.더 큰 버퍼는 적어도 더 작은 버퍼보다 느리지는 않습니다.

const Int32 BufferSize = 128;
using (var fileStream = File.OpenRead(fileName))
  using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize)) {
    String line;
    while ((line = streamReader.ReadLine()) != null)
    {
      // Process line
    }
  }

FileStream생성자를 사용하여 파일 옵션을 지정할 수 있습니다.예를 들어 대용량 파일을 처음부터 끝까지 순차적으로 읽는 경우 다음과 같은 이점을 얻을 수 있습니다.FileOptions.SequentialScan다시 말하지만, 벤치마킹은 여러분이 할 수 있는 최선의 방법입니다.

파일 사용.줄 읽기

은 이는사솔매과우유다니사합루은솔을 을 제외하면 합니다.StreamReader고정 버퍼 크기가 1,024입니다.내 컴퓨터에서는 버퍼 크기가 128인 당신의 코드에 비해 약간 더 나은 성능을 보여줍니다.그러나 더 큰 버퍼 크기를 사용해도 동일한 성능 향상을 얻을 수 있습니다.이 방법은 반복기 블록을 사용하여 구현되며 모든 라인에 대해 메모리를 사용하지 않습니다.

var lines = File.ReadLines(fileName);
foreach (var line in lines)
  // Process line

파일 사용.모든 줄 읽기

이 방법은 반환된 줄 배열을 만드는 데 사용되는 문자열 목록을 증가시켜 메모리 요구 사항을 높인다는 점을 제외하면 이전 방법과 매우 유사합니다.그러나, 그것은 돌아옵니다.String[] 혀가 IEnumerable<String>임의로 라인에 액세스할 수 있습니다.

var lines = File.ReadAllLines(fileName);
for (var i = 0; i < lines.Length; i += 1) {
  var line = lines[i];
  // Process line
}

문자열 사용.분열되다

은 적어도 큰511KB 파일에서 느립니다. 이방법적파에일빅서상느당서(511KB 파일에서 테스트됨) 방식 입니다. 아마도 다음과 같은 이유 때문일 것입니다.String.Split또한 .또한 모든 라인에 어레이를 할당하여 솔루션에 비해 필요한 메모리를 늘립니다.

using (var streamReader = File.OpenText(fileName)) {
  var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
  foreach (var line in lines)
    // Process line
}

저의 제안은 깨끗하고 효율적이기 때문에 사용하는 것입니다.특수 공유 옵션이 필요한 경우(예: 사용)FileShare.ReadWrite할 수 크기를 .), 를 참조하십시오.

를 사용하는 경우.NET 4, 당신을 위해 모든 것을 해주는 것을 사용하세요.저는 그것이 또한 사용할 수 있고 더 큰 버퍼(128개는 매우 작은 것처럼 보입니다)를 제외하고는 당신의 것과 거의 같다고 의심합니다.

하는 동안에File.ReadAllLines()파일을 읽는 가장 간단한 방법 중 하나이며 가장 느린 방법 중 하나이기도 합니다.

이러한 벤치마크에 따르면 파일에서 줄을 많이 읽지 않고 읽기를 원할 경우 파일을 가장 빠르게 읽는 방법은 다음과 같은 오래된 방법입니다.

using (StreamReader sr = File.OpenText(fileName))
{
        string s = String.Empty;
        while ((s = sr.ReadLine()) != null)
        {
               //do minimal amount of work here
        }
}

그러나 각 줄에 많은 작업을 수행해야 하는 경우에는 다음과 같은 방법이 가장 효과적이라고 결론짓습니다(읽을 줄 수 있는 줄 수를 알고 있는 경우에는 문자열을 사전 할당하는 것이 더 빠름).

AllLines = new string[MAX]; //only allocate memory here

using (StreamReader sr = File.OpenText(fileName))
{
        int x = 0;
        while (!sr.EndOfStream)
        {
               AllLines[x] = sr.ReadLine();
               x += 1;
        }
} //Finished. Close the file

//Now parallel process each line in the file
Parallel.For(0, AllLines.Length, x =>
{
    DoYourStuff(AllLines[x]); //do your work here
});

다음 코드를 사용합니다.

foreach (string line in File.ReadAllLines(fileName))

이것은 읽기 성능에 있어서 큰 차이였습니다.

메모리 소비 비용이 들지만, 그에 걸맞는 가치가 있습니다!

파일 크기가 크지 않으면 전체 파일을 읽고 나중에 분할하는 것이 더 빠릅니다.

var filestreams = sr.ReadToEnd().Split(Environment.NewLine, 
                              StringSplitOptions.RemoveEmptyEntries);

스택 오버플로 문제에 대한 좋은 주제가 있습니다. '수익률'이 "오래된 학교" 수익률보다 느립니까?

다음과 같이 표시됩니다.

ReadAllLines는 모든 라인을 메모리에 로드하고 문자열[]을 반환합니다.파일이 작으면 좋습니다.파일이 메모리에 저장할 수 있는 크기보다 크면 메모리가 부족합니다.

반면, ReadLines는 수율 반환을 사용하여 한 번에 한 줄씩 반환합니다.그것으로, 당신은 어떤 크기의 파일도 읽을 수 있습니다.전체 파일을 메모리에 로드하지 않습니다.

"foo"라는 단어가 포함된 첫 번째 줄을 찾고 종료했다고 합니다.ReadAllLines를 사용하면 첫 번째 줄에 "foo"가 발생하더라도 전체 파일을 메모리로 읽어야 합니다.ReadLines에서는 한 줄만 읽습니다.어떤 것이 더 빠를까요?

메모리가 충분하면 전체 파일을 메모리 스트림으로 읽은 다음 스트림 판독기를 열어 줄을 읽음으로써 성능이 향상되었습니다.어쨌든 전체 파일을 실제로 읽을 계획이 있는 한, 이것은 약간의 개선을 가져올 수 있습니다.

기존 API를 사용하여 줄을 읽으려면 더 이상 빨라질 수 없습니다.그러나 더 큰 청크를 읽고 읽기 버퍼에서 각각의 새 줄을 수동으로 찾는 것이 아마도 더 빠를 것입니다.

거대한 텍스트 파일을 효율적으로 읽고 처리해야 하는 경우, ReadLines() 및 ReadAllLines()는 Out of Memory 예외를 발생시킬 가능성이 높습니다.반면에, 각 행을 따로 읽는 것은 시간이 오래 걸릴 것입니다.해결책은 아래와 같이 블록 단위로 파일을 읽는 것이었습니다.

클래스:

    //can return empty lines sometimes
    class LinePortionTextReader
    {
        private const int BUFFER_SIZE = 100000000; //100M characters
        StreamReader sr = null;
        string remainder = "";

        public LinePortionTextReader(string filePath)
        {
            if (File.Exists(filePath))
            {
                sr = new StreamReader(filePath);
                remainder = "";
            }
        }

        ~LinePortionTextReader()
        {
            if(null != sr) { sr.Close(); }
        }

        public string[] ReadBlock()
        {
            if(null==sr) { return new string[] { }; }
            char[] buffer = new char[BUFFER_SIZE];
            int charactersRead = sr.Read(buffer, 0, BUFFER_SIZE);
            if (charactersRead < 1) { return new string[] { }; }
            bool lastPart = (charactersRead < BUFFER_SIZE);
            if (lastPart)
            {
                char[] buffer2 = buffer.Take<char>(charactersRead).ToArray();
                buffer = buffer2;
            }
            string s = new string(buffer);
            string[] sresult = s.Split(new string[] { "\r\n" }, StringSplitOptions.None);
            sresult[0] = remainder + sresult[0];
            if (!lastPart)
            {
                remainder = sresult[sresult.Length - 1];
                sresult[sresult.Length - 1] = "";
            }
            return sresult;
        }

        public bool EOS
        {
            get
            {
                return (null == sr) ? true: sr.EndOfStream;
            }
        }
    }

사용 예:

    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length < 3)
            {
                Console.WriteLine("multifind.exe <where to search> <what to look for, one value per line> <where to put the result>");
                return;
            }

            if (!File.Exists(args[0]))
            {
                Console.WriteLine("source file not found");
                return;
            }
            if (!File.Exists(args[1]))
            {
                Console.WriteLine("reference file not found");
                return;
            }

            TextWriter tw = new StreamWriter(args[2], false);

            string[] refLines = File.ReadAllLines(args[1]);

            LinePortionTextReader lptr = new LinePortionTextReader(args[0]);
            int blockCounter = 0;
            while (!lptr.EOS)
            {
                string[] srcLines = lptr.ReadBlock();
                for (int i = 0; i < srcLines.Length; i += 1)
                {
                    string theLine = srcLines[i];
                    if (!string.IsNullOrEmpty(theLine)) //can return empty lines sometimes
                    {
                        for (int j = 0; j < refLines.Length; j += 1)
                        {
                            if (theLine.Contains(refLines[j]))
                            {
                                tw.WriteLine(theLine);
                                break;
                            }
                        }
                    }
                }

                blockCounter += 1;
                Console.WriteLine(String.Format("100 Mb blocks processed: {0}", blockCounter));
            }
            tw.Close();
        }
    }

문자열 분할 및 어레이 처리를 크게 개선할 수 있다고 생각하지만, 여기서의 목표는 Disk 읽기 수를 최소화하는 것이었습니다.

언급URL : https://stackoverflow.com/questions/8037070/whats-the-fastest-way-to-read-a-text-file-line-by-line

반응형