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


319

텍스트 파일을 한 줄씩 읽고 싶습니다. .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
}

7
으로 Fastest하면 성능이나 개발 관점에서 의미?
sll

1
메소드가 지속되는 동안 파일을 잠글 것입니다. File.ReadAllLines를 배열로 사용한 다음 배열을 처리 할 수 ​​있습니다.
Kell

17
BTW, 묶으 filestream = new FileStreamusing()문은 잠겨있는 파일 핸들 수 성가신 문제를 방지하기 위해
SLL

: FileStream 객체 () 문을 사용 둘러싸과 관련, StackOverflow의 추천 방법에 관한 참조 StackOverflow의 문을 파일 스트림에서는 StreamReader를 사용하여
deegee

ReadToEnd ()가 더 빠르다고 생각합니다.
Dan Gifford

답변:


315

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

StreamReader.ReadLine 사용

이것은 기본적으로 당신의 방법입니다. 어떤 이유로 버퍼 크기를 가능한 가장 작은 값 (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을 . 예를 들어, 큰 파일을 처음부터 끝까지 순차적으로 읽는 경우 이점이있을 수 있습니다 FileOptions.SequentialScan. 다시 한 번 벤치마킹이 최선의 방법입니다.

File.ReadLines 사용

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

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

File.ReadAllLines 사용

이 방법은 반환 된 행 배열을 만드는 데 사용되는 문자열 목록을 늘려 메모리 요구 사항이 더 높다는 점을 제외하면 이전 방법과 매우 유사합니다. 그러나 임의로 리턴 하여 회선에 액세스 할 수 는 String[]없습니다 IEnumerable<String>.

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

String.Split 사용

이 방법은 아마도 String.Split구현 방법으로 인해 적어도 큰 파일 (511KB 파일에서 테스트 됨)에서 상당히 느립니다 . 또한 솔루션에 비해 필요한 메모리를 늘리는 모든 라인에 배열을 할당합니다.

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

File.ReadLines깨끗하고 효율적이기 때문에 사용 하는 것이 좋습니다. 특별한 공유 옵션이 필요한 경우 (예 FileShare.ReadWrite:) 자체 코드를 사용할 수 있지만 버퍼 크기를 늘려야합니다.


1
StreamReader의 생성자에 버퍼 크기 매개 변수를 포함시키는 것이 정말 도움이되었습니다. Amazon의 S3 API에서 스트리밍 중이며 일치하는 버퍼 크기를 사용하면 ReadLine ()과 함께 속도가 상당히 빨라집니다.
Richard K.

이해가 안 돼요 이론적으로 파일을 읽는 데 소요되는 대부분의 시간은 디스크에서 탐색하는 시간과 File.ReadLines에서 수행하는 것과 같은 스트림 조작의 오버 헤드입니다. 반면 File.ReadLines는 한 번에 파일의 모든 내용을 메모리로 읽어야합니다. 성능이 어떻게 나빠질 수 있습니까?
h9uest

2
속도 성능에 대해서는 말할 수 없지만 한 가지 확실합니다. 메모리 소비가 훨씬 더 나쁩니다. 매우 큰 파일 (예 : GB)을 처리해야하는 경우 매우 중요합니다. 그것이 메모리를 교체해야한다는 것을 의미한다면 훨씬 더. 속도 측면에서 결과 지연 처리를 리턴하기 전에 ReadAllLine이 모든 행을 읽어야한다고 추가 할 수 있습니다. 일부 시나리오에서는 속도의 인상이 원 속도보다 더 중요합니다.
bkqc

스트림을 바이트 배열로 읽는 경우 (내가 한 테스트에서) 파일을 20 % ~ 80 % 빠르게 읽습니다 . 바이트 배열을 가져 와서 문자열로 변환하는 것이 필요합니다. 그것이 내가 한 방법입니다 : 읽기 사용 stream.Read () 덩어리로 읽도록 루프를 만들 수 있습니다. 전체 내용을 바이트 배열에 추가 한 후 ( System.Buffer.BlockCopy 사용 ) 바이트를 문자열로 변환해야합니다. Encoding.Default.GetString (byteContent, 0, byteContent.Length-1) .Split (new string [ ] { "\ r \ n", "\ r", "\ n"}, StringSplitOptions.None);
Kim Lage

200

.NET 4를 사용 File.ReadLines하는 경우 간단하게 사용 하십시오. 나는 그것이 또한 사용 하고 큰 버퍼 (128은 매우 작은 것)를 제외하고는 당신과 거의 동일 하다고 생각합니다 FileOptions.SequentialScan.


또 다른 이점은 ReadLines()게 으르므로 LINQ와 잘 작동한다는 것입니다.
stt106

35

File.ReadAllLines()파일을 읽는 가장 간단한 방법 중 하나 이지만 가장 느린 방법 중 하나입니다.

이 벤치 마크에 따르면 많은 일을하지 않고 파일에서 행 을 읽으려면 파일을 읽는 가장 빠른 방법은 다음과 같은 오래된 방법입니다.

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

그러나 각 줄을 많이 사용해야하는 경우이 기사에서는 가장 좋은 방법은 다음과 같다는 결론을 내립니다 (그리고 읽을 줄 수를 알고 있으면 string []을 미리 할당하는 것이 더 빠릅니다).

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
});


5

스택 오버플로 질문에서 이것에 대해 좋은 주제가 있습니다. '수율 반환'이 "구식"반환보다 느립니까? .

그것은 말한다 :

ReadAllLines는 모든 행을 메모리에로드하고 문자열 []을 반환합니다. 파일이 작 으면 좋습니다. 파일이 메모리에 맞는 것보다 큰 경우 메모리가 부족합니다.

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

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


4

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

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

6
File.ReadAllLines()
jgauffin

@jgauffin 나는 file.ReadAlllines () 구현 뒤에 몰라하지만 버퍼가 제한되어 있고 fileReadtoEnd 버퍼가 커야한다고 생각하므로 파일에 대한 액세스 수는이 방법으로 줄어들고 문자열을 수행합니다. 파일 크기가 크지 않은 경우 파일에 대한 다중 액세스보다 빠릅니다.
Saeed Amiri

File.ReadAllLines파일 크기가 알려져 있기 때문에 버퍼 크기가 고정되어 있는지 의심합니다 .
jgauffin

1
@jgauffin : .NET 4.0 File.ReadAllLines에서는 StreamReader.ReadLine(기본 배열을 재 할당 할 수있는)를 사용하여 목록을 만들고 루프에이 목록에 추가합니다 . 이 방법은 기본 버퍼 크기 인 1024를 사용합니다. StreamReader.ReadToEnd줄 구문 분석 부분을 피하고 원하는 경우 생성자에서 버퍼 크기를 설정할 수 있습니다.
Martin Liversage

파일 크기와 관련하여 "BIG"를 정의하면 도움이됩니다.
Paul

2

메모리가 충분하면 전체 파일을 메모리 스트림 으로 읽은 다음 스트림 리더를 열어 줄을 읽음 으로써 성능이 약간 향상되었습니다 . 어쨌든 전체 파일을 실제로 읽을 계획이라면 몇 가지 개선이 이루어질 수 있습니다.


1
File.ReadAllLines그때 더 나은 선택 인 것 같습니다.
jgauffin

2

기존 API를 사용하여 행을 읽으려는 경우 더 빨리 얻을 수 없습니다. 그러나 더 큰 청크를 읽고 읽기 버퍼에서 각각의 새로운 줄을 수동으로 찾는 것이 더 빠를 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.