2010-01-19 2 views
13

특정 문자열을 검색하는 데 필요한 큰 텍스트 파일이 있습니다. 줄 단위로 읽지 않고이를 수행 할 수있는 빠른 방법이 있습니까?C#에서 줄 바꿈없이 큰 텍스트 파일에서 문자열을 검색하는 방법은 무엇입니까?

이 방법은 파일 크기 (10  MB 이상)로 인해 매우 느립니다.

+6

프로그램을 프로파일 해 보셨습니까? –

+5

이 파일이 자주 변경 되나요? 아니면 정적입니까? 정적이라면 런타임에 문서의 필수 하위 섹션에 빠르게 도달 할 수 있도록 오프라인 알고리즘을 작성하고 인덱싱 할 수 있습니다. – Polaris878

+0

나는 파일을 부분적으로 메모리에 읽어 들이기위한 많은 제안을 보았다. 그러나 검색 용어가 한 파일 세그먼트에서 시작하고 다른 파일 세그먼트에서 끝나는 경우를 어떻게 다룰 것인가? 겹쳐진 세그먼트를로드하십시오.이 경우, 다음 청크 읽기에는 전체 용어가 포함되어야합니다. – ProfK

답변

7

파일의 크기를 감안할 때 미리 전체 메모리에 미리 읽고 싶습니까? 라인별로 라인이 가장 좋은 방법 일 것 같습니다.

2

모든 경우에 모든 파일을 검토해야합니다.

조회 Rabin-Karp string search 또는 유사합니다.

+1

검색 할 때마다 반드시 그런 것은 아닙니다. 동일한 파일을 여러 번 검색하는 경우 파일 색인을 작성하는 것이 좋습니다. 따라서 빠른 조회를 수행 할 수 있도록 전체 파일에 대해 단일 통과 만 있으면됩니다. –

0

줄 단위 읽기 속도를 높이려면 큐 기반 응용 프로그램을 만들 수 있습니다.
한 스레드가 줄을 읽고이를 스레드 안전 큐로 채 웁니다. 그런 다음 두 번째 문자열을 처리 할 수 ​​있습니다.

0

특정 문자열을 검색하는 데 필요한 큰 텍스트 파일이 있습니다. 줄 단위로 읽지 않고이를 수행 할 수있는 빠른 방법이 있습니까?

전체 파일을 검색하지 않으려면 미리 입력을 정렬하거나 구성해야합니다. 예를 들어, 이것이 XML 파일이고 이러한 검색을 많이해야하는 경우 XML 파일을 DOM 트리로 파싱하는 것이 좋습니다. 또는 이것이 단어 목록이고 "aero"문자로 시작하는 모든 단어를 찾고 있다면 같은 파일에서 많은 검색을하면 입력 전체를 먼저 정렬하는 것이 좋습니다. .

0

여기서 속도 문제는 검색을 수행하기 전에 파일을 메모리에로드하는 속도 일 수 있습니다. 응용 프로그램을 프로파일 링하여 병목 현상이 어디인지 확인하십시오. 파일을로드하는 경우 파일로드를 "청킹"하여 파일이 작은 청크로 스트리밍되고 각 청크에서 검색이 수행되도록 할 수 있습니다.

찾을 문자열의 일부가 파일 끝에 있으면 분명히 성능이 향상되지 않습니다.

1

원하는 모든 제약 조건까지 파일에서 많은 양의 데이터를 한 번에 메모리로 버퍼링 한 다음 문자열을 검색 할 수 있습니다.

이렇게하면 파일 읽기 횟수가 줄어들고 더 빠른 방법이 될 수 있지만 버퍼 크기를 너무 높게 설정하면 메모리 사용량이 늘어납니다.

1

검색 문자열의 끝에 도달 할 때까지 검색 문자열의 각 문자와 일치하는 문자로 파일을 읽을 수 있어야합니다. 일치하는 경우가 있습니다. 읽은 문자가 찾고자하는 문자와 일치하지 않는 경우 일치하는 수를 0으로 재설정하고 다시 시작하십시오. 예 (**** 의사 /하지 테스트 ****)의 경우 :

(이 길이 체크 한으로 해제 할 수 있지만) 당신이 사용할 수있는 많은 알고리즘 중 하나입니다
byte[] lookingFor = System.Text.Encoding.UTF8.GetBytes("hello world"); 
int index = 0; 
int position = 0; 
bool matchFound = false; 

using (FileStream fileStream = new FileStream(fileName, FileMode.Open)) 
{ 
    while (fileStream.ReadByte() == lookingFor[index]) 
    { 
    index++; 

    if (index == lookingFor.length) 
    { 
     matchFound = true; 
     position = File.position - lookingFor.length; 
     break; 
    } 
    } 
} 

. 첫 번째 일치를 찾을 수 있으므로 다른 루프에서 while 루프를 여러 줄로 묶어서 여러 일치 항목을 찾으려는 것일 수 있습니다.

또한 한 줄씩 파일을 읽는 것에 대해주의해야 할 점은 일치하는 원하는 문자열이 범위를 넘으면 찾을 수 없다는 것입니다.괜찮 으면 한 줄 한 줄씩 검색 할 수 있지만 줄 바꿈을 위해 검색 문자열이 필요한 경우 위에서 설명한 알고리즘을 사용하는 것이 좋습니다.

마지막으로 가장 빠른 속도를 찾고 있다면 위의 코드를 StreamReader 또는 다른 버퍼링 된 리더를 사용하도록 마이그레이션하는 것이 좋습니다.

1

프로젝트가 매번 같은 문자열이나 다른 문자열로 다른 파일을 검색하거나 매번 동일한 문자열을 찾기 위해 프로젝트를 수행해야합니까?

후자 인 경우 파일 색인을 작성할 수 있습니다. 그러나 파일을 자주 변경하면 색인을 작성하는 데 많은 비용이 듭니다.

전체 텍스트 검색을 위해 파일의 색인을 생성하려면 Lucene.NET 라이브러리를 사용할 수 있습니다.

http://incubator.apache.org/lucene.net/

+0

참고하시기 바랍니다. 링크가 깨졌습니다. – musefan

0

당신은 단지 특정 문자열을 찾고 있다면, 나는 한 줄 한 줄 최고의 가장 효율적인 메커니즘이다라고 말하고 싶지만. 반면에 응용 프로그램의 여러 지점에서 특히 여러 문자열을 찾으려면 Lucene.Net을 조사하여 색인을 만든 다음 색인을 쿼리하는 것이 좋습니다. 이것이 일회성 실행 인 경우 (즉, 나중에 동일한 파일을 다시 질의 할 필요가없는 경우) 시스템에서 자동으로 정리할 임시 파일에 인덱스를 생성 할 수 있습니다 (일반적으로 부팅 시간 또는 프로그램을 종료 할 때 직접 삭제할 수 있습니다.) 나중에 같은 파일을 다시 검색해야하는 경우, 알려진 위치에 색인을 저장하고 두 번째로 훨씬 좋은 성능을 얻을 수 있습니다.

0

SQL Server 2005/2008에 고정시키고 전체 텍스트 검색 기능을 사용하십시오.

3

다음은 스트림을 사용하여 한 번에 한 문자 씩 읽는 내 솔루션입니다. 전체 값을 찾을 때까지 한 번에 한 문자 씩 값을 검색하기 위해 사용자 지정 클래스를 만들었습니다.

네트워크 드라이브에 저장된 100MB 파일을 사용하여 몇 가지 테스트를 실시했으며 속도는 파일에서 읽을 수있는 속도에 완전히 달려있었습니다. Windows에서 파일이 버퍼링 된 경우 전체 파일을 검색하는 데 3 초도 걸리지 않습니다. 그렇지 않으면 네트워크 속도에 따라 7 초에서 60 초 정도 걸릴 수 있습니다.

메모리의 String에 대해 실행될 때 검색 자체가 1 초 미만이었으며 일치하는 문자가 없었습니다. 많은 주요 문자가 일치하는 경우 검색이 더 오래 걸릴 수 있습니다.

public static int FindInFile(string fileName, string value) 
{ // returns complement of number of characters in file if not found 
    // else returns index where value found 
    int index = 0; 
    using (System.IO.StreamReader reader = new System.IO.StreamReader(fileName)) 
    { 
     if (String.IsNullOrEmpty(value)) 
      return 0; 
     StringSearch valueSearch = new StringSearch(value); 
     int readChar; 
     while ((readChar = reader.Read()) >= 0) 
     { 
      ++index; 
      if (valueSearch.Found(readChar)) 
       return index - value.Length; 
     } 
    } 
    return ~index; 
} 
public class StringSearch 
{ // Call Found one character at a time until string found 
    private readonly string value; 
    private readonly List<int> indexList = new List<int>(); 
    public StringSearch(string value) 
    { 
     this.value = value; 
    } 
    public bool Found(int nextChar) 
    { 
     for (int index = 0; index < indexList.Count;) 
     { 
      int valueIndex = indexList[index]; 
      if (value[valueIndex] == nextChar) 
      { 
       ++valueIndex; 
       if (valueIndex == value.Length) 
       { 
        indexList[index] = indexList[indexList.Count - 1]; 
        indexList.RemoveAt(indexList.Count - 1); 
        return true; 
       } 
       else 
       { 
        indexList[index] = valueIndex; 
        ++index; 
       } 
      } 
      else 
      { // next char does not match 
       indexList[index] = indexList[indexList.Count - 1]; 
       indexList.RemoveAt(indexList.Count - 1); 
      } 
     } 
     if (value[0] == nextChar) 
     { 
      if (value.Length == 1) 
       return true; 
      indexList.Add(1); 
     } 
     return false; 
    } 
    public void Reset() 
    { 
     indexList.Clear(); 
    } 
} 
2

가장 빠른 검색 방법은 Boyer-Moore algorithm입니다. 이 메서드는 파일에서 모든 바이트를 읽을 필요는 없지만 바이트에 임의 액세스해야합니다. 또한이 방법은 구현이 간단합니다.

1

Wayne Cornish가 이미 말했듯이 : 라인별로 한 줄씩 읽는 것이 가장 좋은 방법 일 수 있습니다.

예를 들어 전체 파일을 문자열로 읽은 다음 정규 표현식으로 검색하면보다 우아하지만 더 큰 문자열 객체를 만들 수 있습니다.

이러한 종류의 개체는 Large Object Heap (LOH, 85.000 바이트를 초과하는 개체의 경우)에 저장되므로 문제가 발생할 수 있습니다. 이러한 큰 파일을 많이 파싱하고 메모리가 제한되어 있으면 (x86) LOH 단편화 문제가 발생할 수 있습니다.

많은 큰 파일을 구문 분석하면 줄 단위로 읽는 것이 좋습니다!

1

다음은 문자 단위로 읽는 간단한 한 가지 해결책입니다. 나를 위해 잘 일했다.

/// <summary> 
/// Find <paramref name="toFind"/> in <paramref name="reader"/>. 
/// </summary> 
/// <param name="reader">The <see cref="TextReader"/> to find <paramref name="toFind"/> in.</param> 
/// <param name="toFind">The string to find.</param> 
/// <returns>Position within <paramref name="reader"/> where <paramref name="toFind"/> starts or -1 if not found.</returns> 
/// <exception cref="ArgumentNullException">When <paramref name="reader"/> is null.</exception> 
/// <exception cref="ArgumentException">When <paramref name="toFind"/> is null or empty.</exception> 
public int FindString(TextReader reader, string toFind) 
{ 
    if(reader == null) 
     throw new ArgumentNullException("reader"); 

    if(string.IsNullOrEmpty(toFind)) 
     throw new ArgumentException("String to find may not be null or empty."); 

    int charsRead = -1; 
    int pos = 0; 
    int chr; 

    do 
    { 
     charsRead++; 
     chr = reader.Read(); 
     pos = chr == toFind[pos] ? pos + 1 : 0; 
    } 
    while(chr >= 0 && pos < toFind.Length); 

    int result = chr < 0 ? -1 : charsRead - toFind.Length; 
    return result < 0 ? -1 : result; 
} 

희망이 있습니다.

관련 문제