+1

Tối ưu hóa tìm kiếm chuỗi với SearchValues trong .NET Core 8

.NET Core 8 giới thiệu SearchValues, một cấu trúc mạnh mẽ giúp cải thiện hiệu suất tìm kiếm chuỗi. Bài viết này sẽ hướng dẫn bạn cách sử dụng SearchValues và so sánh nó với phương pháp IndexOf truyền thống.

Giới thiệu về SearchValues

Thông thường, khi cần xác định xem một chuỗi có chứa nhiều từ khóa/từ hay không, lập trình viên thường sử dụng đoạn mã như sau:

public static class Extensions
{
    public static bool Search(this string line) =>
        line.IndexOf("hello", StringComparison.OrdinalIgnoreCase) > 1 && 
        line.IndexOf("world", StringComparison.OrdinalIgnoreCase) > 1;
}

Tuy nhiên, bắt đầu từ .NET Core 8, Microsoft đã cung cấp System.Buffers.SearchValues<T> Class. Vậy nên sử dụng IndexOf hay SearchValues? SearchValues là một cấu trúc mạnh mẽ giúp cải thiện hiệu suất của các hoạt động tìm kiếm.

Việc cung cấp một phương thức chuyên dụng và được tối ưu hóa cho việc tra cứu giúp bạn viết mã hiệu quả và gọn gàng hơn, đặc biệt là trong các trường hợp thường xuyên kiểm tra nhiều giá trị.

SearchValues không phải là sự thay thế hoàn toàn cho IndexOf hoặc IndexOfAny. SearchValues hoạt động hiệu quả hơn trên các chuỗi lớn, còn đối với các chuỗi nhỏ, lập trình viên vẫn có thể sử dụng IndexOf và IndexOf, v.v.

Ví dụ 1: Kiểm tra spam trong văn bản

Giả sử chúng ta có một tệp văn bản TextBanned.txt và một tệp json bannedwords.json chứa các từ khóa cần kiểm tra.

TextBanned.txt

Hello Karen, I am writing to inform you that your account is now active.
This is not a spam message. Please click the link below

Trong một dự án, tệp json được sử dụng cho các mã thông báo/từ được theo dõi.

banwords.json

[
  {
    "Id": "1",
    "Name": "spam"
  },
  {
    "Id": "2",
    "Name": "advertisement"
  },
  {
    "Id": "3",
    "Name": "clickbait"
  }
]

Chúng ta sẽ sử dụng model BannedWord để deserialize tệp bannedwords.json:

public class BannedWord
{
    public string Id { get; set; }
    public string Name { get; set; }
}

Tiếp theo, tạo một phương thức mở rộng cho SearchValues:

public static class GenericExtensions
{
    /// <summary>
    /// Determines whether the specified text contains any of the banned words.
    /// </summary>
    /// <param name="text">The text to be checked for banned words.</param>
    /// <param name="bannedWords">An array of banned words to search for within the text.</param>
    /// <returns>
    /// <c>true</c> if the text contains any of the banned words; otherwise, <c>false</c>.
    /// </returns>
    public static bool HasBannedWords(this string text, params string[] bannedWords) => 
        text.AsSpan().ContainsAny(SearchValues.Create(bannedWords, StringComparison.OrdinalIgnoreCase));
}

Lưu ý: Phương thức mở rộng trên không phân biệt chữ hoa chữ thường. Bạn có thể thêm logic và tham số bool để xác định xem tìm kiếm có phân biệt chữ hoa chữ thường hay không hoặc tạo một phương thức quá tải cho việc khớp case.

Đoạn mã sau đọc các từ/từ khóa cần tìm kiếm bằng cách deserialize bannedwords.json, sau đó đọc tệp TestBanneded.txt để quét spam. Vòng lặp foreach sử dụng Enumerable.Index (trong .NET Core 9) cho phép phân tách chỉ mục hiện tại (bắt đầu từ 0) và mục, trong đó mục là một dòng cho biến sentences. Debug.WriteLine được sử dụng bên dưới vì mã nguồn được thực hiện trong một dự án Windows Forms, nơi Console.WriteLine không hoạt động.

Ví dụ 2: Tìm lỗi/cảnh báo trong tệp nhật ký Visual Studio

Khi Visual Studio gặp lỗi, chúng có thể được ghi vào tệp nhật ký bằng cách khởi động Visual Studio với lệnh devenv.exe /log. Mở tệp ActivityLog.xml, thường có hàng nghìn dòng và việc tìm kiếm lỗi/cảnh báo có thể rất tốn thời gian.

Dưới đây là một phần nhỏ của tệp ActivityLog.xml: image.png

Các phương thức mở rộng sau, phương thức thứ nhất và thứ hai sử dụng SearchValues, trong đó phương thức thứ hai sẽ được sử dụng trong ví dụ này vì chúng ta chỉ quan tâm đến lỗi và cảnh báo. Phương thức mở rộng thứ nhất sẽ được sử dụng cho các tìm kiếm mục đích chung. Phương thức mở rộng cuối cùng là cách tiếp cận thông thường, ít linh hoạt hơn.

public static class Extensions
{
    /// <summary>
    /// Searches the specified string for any of the provided tokens case-insensitive.
    /// </summary>
    /// <param name="sender">The string to search within.</param>
    /// <param name="tokens">An array of tokens to search for within the string.</param>
    /// <returns>
    /// <c>true</c> if any of the tokens are found within the string; otherwise, <c>false</c>.
    /// </returns>
    public static bool Search(this string sender, string[] tokens) 
        => sender.AsSpan().ContainsAny(
            SearchValues.Create(tokens, 
                StringComparison.OrdinalIgnoreCase));

    /// <summary>
    /// Determines whether the specified line contains a warning or error.
    /// </summary>
    /// <param name="line">The line of text to be checked for warnings or errors.</param>
    /// <returns>
    /// <c>true</c> if the line contains a warning or error; otherwise, <c>false</c>.
    /// </returns>
    public static bool LineHasWarningOrError(this string line)
    {
        ReadOnlySpan<string> tokens = ["<type>Error</type>", "<type>Warning</type>"];
        return line.AsSpan().ContainsAny(SearchValues.Create(tokens, StringComparison.OrdinalIgnoreCase));
    }

    /// <summary>
    /// Determines whether the specified line contains a warning or error using conventional string comparison.
    /// </summary>
    /// <param name="line">The line of text to be checked for warnings or errors.</param>
    /// <returns>
    /// <c>true</c> if the line contains a warning or error; otherwise, <c>false</c>.
    /// </returns>
    public static bool LineHasWarningOrErrorConventional(this string line) =>
        line.IndexOf("<type>Error</type>", StringComparison.OrdinalIgnoreCase) > 1 && 
        line.IndexOf("<type>Warning</type>", StringComparison.OrdinalIgnoreCase) > 1;
}

Thực thi mã (mã nguồn đầy đủ được cung cấp):

  • Xác định xem tệp activity có tồn tại hay không, nếu có thì đọc nó.
  • Hiển thị đường dẫn và tên tệp cùng với số dòng.
  • Lặp lại từng dòng để tìm kiếm lỗi và cảnh báo.

image.png

Phần bổ sung: Việc tìm kiếm tệp nhật ký activity không dễ dàng và có thể có nhiều tệp. Để hỗ trợ việc tìm kiếm tệp nhật ký activity phù hợp, mã nguồn được cung cấp có một lớp chuyên dụng để làm việc với tệp activity, bao gồm cung cấp đường dẫn đến tệp activity, điều này có thể hữu ích cho các nhà phát triển muốn kiểm tra các tệp activity cũ hơn.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.