.NET 시스템이다.OutOfMemoryException 에서 문자열입니다.Split()120MB CSV 파일

StackOverflow https://stackoverflow.com/questions/809156

  •  03-07-2019
  •  | 
  •  

문제

내가 사용하는 C#을 읽~120MB 일반 텍스트 CSV 파일입니다.처음에 나는 구문 분석하는 그것을 읽어 줄지만,최근에 결정을 읽고 전체 파일의 내용은 메모리로 먼저 여러 배 빠릅니다.분석이 이미 매우 느리기 때문에 CSV 는 쉼표로 구분 안에 포함된 지수,즉 내가 사용하는 regex 분할됩니다.이것은 하나만 내가 찾는 안정적으로 작동:

string[] fields = Regex.Split(line, 
@",(?!(?<=(?:^|,)\s*\x22(?:[^\x22]|\x22\x22|\\\x22)*,)
(?:[^\x22]|\x22\x22|\\\x22)*\x22\s*(?:,|$))");
// from http://regexlib.com/REDetails.aspx?regexp_id=621

를 수행하기 위해 분석을 읽은 후 전체 내용을 메모리에,나는 분할 문자열에 줄바꿈 문자를 배열을 얻을 포함하는 확인할 수 있게 되었습니다.그러나 내가 이것을 할 때에 120MB 파일을 얻을 System.OutOfMemoryException.왜 기억을 그래서 빠르게 내 컴퓨터가 4GB RAM?더 좋은 방법이 있을 신속하게 분석한 복잡한 CSV?

도움이 되었습니까?

해결책

기본적으로 모든 규모의 할당에 대해 OutofMemoryException을 얻을 수 있습니다. 메모리 조각을 할당하면 요청 된 크기에 대한 지속적인 메모리를 요구합니다. 그것이 영예를 얻지 못하면 OutofMemoryException을 보게 될 것입니다.

또한 64 비트 Wind

.NET에서 문자열 작업을 수행 할 때 .NET 문자열이 불변이기 때문에 많은 임시 문자열을 생성 할 위험이 있습니다. 따라서 메모리 사용이 상당히 급격히 증가 할 수 있습니다.

다른 팁

당신이 필요하지 않으면 자신의 파서를 굴리지 마십시오. 나는 이것으로 운이 좋았다 :

빠른 CSV 리더

다른 것이 없다면 당신은 후드 아래를보고 다른 사람이 어떻게하는지 볼 수 있습니다.

전체 파일이 문자열로 읽은 경우 아마도 StringReader.

StringReader reader = new StringReader(fileContents);
string line;
while ((line = reader.ReadLine()) != null) {
    // Process line
}

이것은 내용이 이미 메모리에있는 차이가있는 파일에서 스트리밍하는 것과 동일해야합니다.

테스트 후 편집

처리가 line.length와 함께 길이 변수를 증가시키는 140MB 파일로 위의 시도를 시도했습니다. 내 컴퓨터에서 약 1.6 초가 걸렸습니다. 그 후 나는 다음을 시도했다.

System.IO.StreamReader reader = new StreamReader("D:\\test.txt");
long length = 0;
string line;
while ((line = reader.ReadLine()) != null)
    length += line.Length;

결과는 약 1 초였습니다.

물론 마일리지가 다를 수 있습니다. 특히 네트워크 드라이브에서 읽거나 처리하는 데 하드 드라이브가 다른 곳을 찾는 데 충분히 오래 걸리는 경우. 그러나 Filestream을 사용하여 파일을 읽고 버퍼링하지 않는 경우도 있습니다. StreamReader는 버퍼링을 제공하여 판독 값을 크게 향상시킵니다.

연속적인 메모리로 단일 객체를 할당하지 않을 수도 있고, 가능하지 않아야 할 수도 없습니다. 스트리밍은 이것을하는 일반적인 방법이지만, 당신은 그것이 느릴 수 있다는 것이 옳습니다 (보통 그다지 느리게해야한다고 생각하지는 않지만).

타협으로, 당신은 다음과 같은 함수로 파일의 더 많은 부분을 한 번에 읽을 수 있습니다. StreamReader.ReadBlock(), 각 부분을 차례로 처리합니다.

다른 포스터가 말했듯이, 외곽은 요청 된 크기에 대한 연속적인 메모리 덩어리를 찾을 수 없기 때문입니다.

그러나 파싱 라인을 라인별로 수행하는 것이 한 번에 모두 읽은 다음 처리하는 것보다 몇 배나 빠르다고 말합니다. 이것은 당신이 블로킹 읽기를 수행하는 순진한 접근법 (예 : 의사 코드)을 추구하는 경우에만 의미가 있습니다.

while(! file.eof() )
{
    string line = file.ReadLine();
    ProcessLine(line);
}

대신 스트리밍을 사용해야합니다. 스트림은 파일을 읽는 대체 스레드에서 쓰기 () 호출로 작성된 스트리밍을 사용해야하므로 파일은 프로세스 라인 ()가하는 일에 따라 읽기가 차단되지 않습니다. 전체 파일을 한 번에 읽은 다음 처리를 수행하는 성능에 따라야합니다.

당신은 아마 시도해야합니다 CLR 프로파일 러 실제 메모리 사용을 결정합니다. 시스템 RAM 이외의 메모리 제한이있을 수 있습니다. 예를 들어 IIS 응용 프로그램 인 경우 메모리는 응용 프로그램 풀에 의해 제한됩니다.

이 프로필 정보를 사용하면 원래 시도했던 CSV 파일 스트리밍과 같은보다 확장 가능한 기술을 사용해야 할 수도 있습니다.

당신은 메모리를 실행하는 스택에 힙.

당신이 시도할 수 있습을 다시 고려해 귀하의 응용 프로그램은 당신이 처리하는 입력에서 더 다루기 쉬운 덩어리보다는 데이터의 처리 120MB 니다.

나는 여기에 대부분의 사람들과 동의합니다. 스트리밍을 사용해야합니다.

나는 지금까지 누군가가 말했는지는 모르겠지만, 당신은 귀사 방법을 살펴 봐야합니다.

그리고 나는 .net / clr의 최고의 CSV 분할 기술은이 하나

이 기술은 입력 CSV에서 ME +10GB XML 출력을 생성했습니다.이 기술은 초과 입력 필터를 포함하여 내가 본 것보다 더 빠릅니다.

버퍼에 청크를 읽고 작업해야합니다. 그런 다음 다른 청크 등을 읽으십시오.

당신을 위해 이것을 효율적으로 할 수있는 많은 도서관이 있습니다. 나는 하나를 유지합니다 CSVHELPER. 쉼표 나 라인 엔딩이 필드 중간에있을 때와 같이 처리해야 할 에지 케이스가 많이 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top