2010-01-19 25 views
13

Tôi có một tệp văn bản lớn mà tôi cần tìm kiếm một chuỗi cụ thể. Có cách nào nhanh chóng để thực hiện việc này mà không đọc từng dòng một không?Làm thế nào để bạn tìm kiếm một tập tin văn bản lớn cho một chuỗi mà không đi từng dòng trong C#?

Phương pháp này cực kỳ chậm do kích thước của tệp (hơn 100   MB).

+6

Bạn đã lược tả chương trình của mình chưa? –

+5

Tệp này có thay đổi thường xuyên hoặc tĩnh không? Nếu nó tĩnh, bạn có thể thực hiện một thuật toán ngoại tuyến và lập chỉ mục nó để bạn có thể nhanh chóng tiếp cận phần con được yêu cầu của tài liệu khi chạy. – Polaris878

+0

Tôi đã thấy rất nhiều đề xuất đọc phần tệp một phần vào bộ nhớ, nhưng làm cách nào bạn xử lý được cụm từ mà cụm từ tìm kiếm bắt đầu trong một phân đoạn tệp và kết thúc bằng một phân đoạn tệp khác. Tải các phân đoạn chồng chéo có lẽ, nếu trường hợp này xảy ra, đoạn tiếp theo được đọc phải chứa toàn bộ cụm từ – ProfK

Trả lời

7

Với kích thước của tệp, bạn có thực sự muốn đọc chúng hoàn toàn vào bộ nhớ trước không? Theo từng dòng có thể là phương pháp tốt nhất ở đây.

2

Trong mọi trường hợp, bạn sẽ phải đi qua tất cả tệp.

Tra cứu Rabin-Karp string search hoặc tương tự.

+1

Không nhất thiết phải mỗi khi bạn tìm kiếm nó. Nếu cùng một tập tin được tìm kiếm rất nhiều lần, nó có thể có ý nghĩa để xây dựng một chỉ mục của tập tin, do đó, sẽ chỉ cần có một pass duy nhất trên toàn bộ tập tin để cho phép bất kỳ số lượng tra cứu nhanh chóng. –

0

Nếu bạn muốn tăng tốc độ đọc theo từng dòng, bạn có thể tạo ứng dụng dựa trên hàng đợi:
Một chủ đề đọc dòng và đặt chúng vào hàng đợi an toàn. Sau đó, người thứ hai có thể xử lý các chuỗi

0

Tôi có một tệp văn bản lớn mà tôi cần tìm kiếm một chuỗi cụ thể. Có cách nào nhanh chóng để thực hiện việc này mà không đọc từng dòng một không?

Cách duy nhất để tránh tìm kiếm trên toàn bộ tệp là sắp xếp hoặc sắp xếp dữ liệu nhập trước đó. Ví dụ, nếu đây là một tệp XML và bạn cần phải thực hiện nhiều trong số các tìm kiếm này, thì sẽ phân tích cú pháp tệp XML thành một cây DOM. Hoặc nếu đây là danh sách các từ và bạn đang tìm kiếm tất cả các từ bắt đầu bằng các chữ cái "aero", bạn có thể sắp xếp toàn bộ đầu vào trước nếu bạn thực hiện rất nhiều loại tìm kiếm trên cùng một tệp .

0

Sự cố tốc độ ở đây có thể là tốc độ được thực hiện để tải tệp vào bộ nhớ trước khi thực hiện tìm kiếm. Hãy thử lược tả ứng dụng của bạn để xem nút cổ chai ở đâu. Nếu nó đang tải tập tin bạn có thể thử "chunking" tải tập tin để các tập tin được streamed trong khối nhỏ và mỗi đoạn có tìm kiếm thực hiện trên đó.

Rõ ràng nếu một phần của chuỗi được tìm thấy ở cuối tệp, sẽ không có hiệu suất.

1

Bạn có thể đệm một lượng lớn dữ liệu từ tệp vào bộ nhớ cùng một lúc, tùy thuộc vào bất kỳ ràng buộc nào bạn muốn và sau đó tìm kiếm nó cho chuỗi.

Điều này sẽ có tác dụng giảm số lần đọc trên tệp và có khả năng là phương pháp nhanh hơn, nhưng sẽ tốn nhiều bộ nhớ nếu bạn đặt kích thước bộ đệm quá cao.

1

Bạn có thể đọc ký tự tệp theo ký tự khớp với từng ký tự trong chuỗi tìm kiếm cho đến khi bạn đến cuối chuỗi tìm kiếm trong trường hợp bạn khớp. Nếu bất kỳ lúc nào, ký tự bạn đã đọc không khớp với ký tự bạn đang tìm kiếm, hãy đặt lại số phù hợp thành 0 và bắt đầu lại. Ví dụ: (**** mã giả/không được thử nghiệm ****):

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

Đó là một trong nhiều thuật toán bạn có thể sử dụng (mặc dù nó có thể được tắt bằng một thuật toán kiểm tra độ dài). Nó sẽ chỉ tìm thấy trận đấu đầu tiên, do đó bạn có thể muốn quấn vòng lặp while trong vòng lặp khác để tìm nhiều kết quả phù hợp.

Ngoài ra, một điều cần lưu ý về việc đọc từng dòng tệp là nếu chuỗi mong muốn khớp với các dòng nhịp bạn sẽ không tìm thấy nó.Nếu đó là tốt thì bạn có thể tìm kiếm từng dòng nhưng nếu bạn cần chuỗi tìm kiếm để mở rộng dòng bạn sẽ muốn sử dụng một thuật toán như tôi đã nêu chi tiết ở trên.

Cuối cùng, nếu bạn đang tìm kiếm tốc độ tốt nhất, có vẻ như bạn đang có, bạn sẽ muốn di chuyển mã ở trên để sử dụng StreamReader hoặc một số trình đọc được đệm khác.

1

Dự án của bạn có cần tìm kiếm các tệp khác nhau cho cùng một chuỗi hoặc chuỗi khác nhau mỗi lần hoặc tìm kiếm cùng một tệp cho các chuỗi khác nhau mỗi lần không?

Nếu sau này, bạn có thể tạo chỉ mục của tệp. Nhưng không có điểm nào làm điều này nếu tập tin thay đổi thường xuyên, bởi vì việc xây dựng chỉ mục sẽ rất tốn kém.

Để lập chỉ mục tệp để tìm kiếm toàn văn bản, bạn có thể sử dụng thư viện Lucene.NET.

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

+0

FYI, liên kết của bạn bị hỏng – musefan

0

Nếu bạn chỉ tìm kiếm một chuỗi cụ thể, tôi muốn nói line-by-line là cơ chế tốt nhất và hiệu quả nhất. Mặt khác, nếu bạn đang tìm kiếm nhiều chuỗi, đặc biệt là tại một số điểm khác nhau trong ứng dụng, bạn có thể muốn xem xét Lucene.Net để tạo chỉ mục và sau đó truy vấn chỉ mục. Nếu đây là lần chạy một lần (nghĩa là, bạn sẽ không cần truy vấn lại cùng một tệp), bạn có thể tạo chỉ mục trong tệp tạm thời sẽ được hệ thống tự động dọn dẹp (thường là thời gian khởi động hoặc bạn có thể tự xóa nó khi chương trình của bạn thoát). Nếu bạn cần phải tìm kiếm cùng một tệp một lần nữa sau đó, bạn có thể lưu chỉ mục ở một vị trí đã biết và nhận được hiệu suất tốt hơn nhiều lần thứ hai xung quanh.

0

Gắn vào SQL Server 2005/2008 và sử dụng khả năng tìm kiếm toàn văn.

3

Đây là giải pháp của tôi sử dụng luồng để đọc trong một ký tự cùng một lúc. Tôi đã tạo một lớp tùy chỉnh để tìm kiếm giá trị một ký tự cùng một lúc cho đến khi tìm thấy toàn bộ giá trị.

Tôi chạy một số thử nghiệm với tệp 100MB được lưu trên ổ đĩa mạng và tốc độ hoàn toàn phụ thuộc vào tốc độ đọc trong tệp. Nếu tệp được đệm trong Windows, tìm kiếm toàn bộ tệp mất chưa tới 3 giây. Nếu không, nó có thể mất từ ​​7 giây đến 60 giây, tùy thuộc vào tốc độ mạng.

Bản thân tìm kiếm mất chưa tới một giây nếu chạy với Chuỗi trong bộ nhớ và không có ký tự trùng khớp. Nếu có nhiều ký tự hàng đầu được tìm thấy phù hợp với tìm kiếm có thể mất nhiều thời gian hơn.

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

Phương pháp nhanh nhất để tìm kiếm là Boyer-Moore algorithm. Phương thức này không đòi hỏi phải đọc tất cả các byte từ các tệp, nhưng yêu cầu quyền truy cập ngẫu nhiên vào các byte. Ngoài ra, phương pháp này là đơn giản trong việc thực hiện.

1

Như Wayne Cornish đã nói: Đọc từng dòng có thể là cách tiếp cận tốt nhất.

Nếu bạn đọc ví dụ toàn bộ tệp thành chuỗi và sau đó tìm kiếm bằng cụm từ thông dụng, nó có thể thanh lịch hơn, nhưng bạn sẽ tạo đối tượng chuỗi lớn.

Các loại đối tượng này có thể gây ra sự cố, vì chúng sẽ được lưu trữ trên Heap đối tượng lớn (LOH, đối với các đối tượng trên 85.000 byte). Nếu bạn phân tích nhiều tệp lớn này và bộ nhớ của bạn bị giới hạn (x86), bạn có thể gặp phải sự cố phân mảnh LOH.

=> Dòng đọc tốt hơn theo từng dòng nếu bạn phân tích cú pháp nhiều tệp lớn!

1

Đây là ký tự đọc giải pháp một chức năng đơn giản theo ký tự. Làm việc tốt cho tôi.

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

Hy vọng điều đó sẽ hữu ích.

Các vấn đề liên quan