Làm cách nào để thực hiện thao tác “bắt đầu với” nhạy cảm với văn hóa từ giữa một chuỗi?


106

Tôi có một yêu cầu tương đối mù mờ, nhưng có vẻ như nó sẽ có thể thực hiện được bằng cách sử dụng BCL.

Đối với ngữ cảnh, tôi đang phân tích cú pháp chuỗi ngày / giờ trong Noda Time . Tôi duy trì một con trỏ logic cho vị trí của tôi trong chuỗi đầu vào. Vì vậy, trong khi chuỗi hoàn chỉnh có thể là "3 tháng 1 năm 2013", con trỏ logic có thể ở 'J'.

Bây giờ, tôi cần phân tích cú pháp tên tháng, so sánh nó với tất cả các tên tháng đã biết cho văn hóa:

  • Văn hóa nhạy cảm
  • Không phân biệt chữ hoa chữ thường
  • Chỉ từ điểm của con trỏ (không phải sau này; tôi muốn xem liệu con trỏ có đang "nhìn" tên tháng ứng cử viên hay không)
  • Mau
  • ... và tôi cần biết sau đó có bao nhiêu ký tự đã được sử dụng

Các mã hiện tại để làm điều này thường hoạt động, sử dụng CompareInfo.Compare. Nó thực sự như thế này (chỉ dành cho phần khớp - có nhiều mã hơn trong thực tế, nhưng nó không liên quan đến phần khớp):

internal bool MatchCaseInsensitive(string candidate, CompareInfo compareInfo)
{
    return compareInfo.Compare(text, position, candidate.Length,
                               candidate, 0, candidate.Length, 
                               CompareOptions.IgnoreCase) == 0;
}

Tuy nhiên, điều đó phụ thuộc vào ứng viên và khu vực mà chúng tôi so sánh có cùng độ dài. Hầu hết thời gian đều tốt, nhưng không tốt trong một số trường hợp đặc biệt. Giả sử chúng ta có một cái gì đó như:

// U+00E9 is a single code point for e-acute
var text = "x b\u00e9d y";
int position = 2;
// e followed by U+0301 still means e-acute, but from two code points
var candidate = "be\u0301d";

Bây giờ so sánh của tôi sẽ thất bại. Tôi có thể sử dụng IsPrefix:

if (compareInfo.IsPrefix(text.Substring(position), candidate,
                         CompareOptions.IgnoreCase))

nhưng:

  • Điều đó đòi hỏi tôi phải tạo một chuỗi con, điều mà tôi thực sự muốn tránh. (Tôi đang xem Noda Time thực sự là một thư viện hệ thống; hiệu suất phân tích cú pháp cũng có thể quan trọng đối với một số khách hàng.)
  • Nó không cho tôi biết bao xa để di chuyển con trỏ sau đó

Trong thực tế, tôi thực sự nghi ngờ điều này sẽ không xuất hiện thường xuyên ... nhưng tôi thực sự muốn làm điều đúng đắn ở đây. Tôi cũng thực sự muốn có thể làm điều đó mà không cần trở thành chuyên gia Unicode hoặc tự thực hiện nó :)

(Được phát triển thành lỗi 210 trong Noda Time, trong trường hợp bất kỳ ai muốn theo dõi bất kỳ kết luận cuối cùng nào.)

Tôi thích ý tưởng bình thường hóa. Tôi cần kiểm tra chi tiết điều đó để biết a) tính đúng đắn và b) hiệu suất. Giả sử tôi có thể làm cho nó hoạt động chính xác, tôi vẫn không chắc liệu nó có đáng để thay đổi hay không - đó là loại điều có thể sẽ không bao giờ thực sự xuất hiện trong đời thực, nhưng có thể ảnh hưởng đến hiệu suất của tất cả người dùng của tôi: (

Tôi cũng đã kiểm tra BCL - dường như cũng không xử lý việc này đúng cách. Mã mẫu:

using System;
using System.Globalization;

class Test
{
    static void Main()
    {
        var culture = (CultureInfo) CultureInfo.InvariantCulture.Clone();
        var months = culture.DateTimeFormat.AbbreviatedMonthNames;
        months[10] = "be\u0301d";
        culture.DateTimeFormat.AbbreviatedMonthNames = months;

        var text = "25 b\u00e9d 2013";
        var pattern = "dd MMM yyyy";
        DateTime result;
        if (DateTime.TryParseExact(text, pattern, culture,
                                   DateTimeStyles.None, out result))
        {
            Console.WriteLine("Parsed! Result={0}", result);
        }
        else
        {
            Console.WriteLine("Didn't parse");
        }
    }
}

Thay đổi tên tháng tùy chỉnh thành chỉ "giường" với giá trị văn bản là "bEd" phân tích cú pháp tốt.

Được rồi, một vài điểm dữ liệu khác:

  • Chi phí sử dụng SubstringIsPrefixlà đáng kể nhưng không kinh khủng. Trong một ví dụ về "Thứ sáu, ngày 12 tháng 4 năm 2013 20:28:42" trên máy tính xách tay phát triển của tôi, nó thay đổi số lượng hoạt động phân tích cú pháp mà tôi có thể thực hiện trong một giây từ khoảng 460 nghìn thành khoảng 400 nghìn. Tôi muốn tránh sự chậm lại đó nếu có thể, nhưng nó không quá tệ.

  • Chuẩn hóa kém khả thi hơn tôi nghĩ - vì nó không có sẵn trong Thư viện lớp di động. Tôi có khả năng có thể sử dụng nó chỉ cho người không PCL xây dựng, cho phép PCL xây dựng được một chút ít chính xác. Hiệu suất đạt được của thử nghiệm chuẩn hóa ( string.IsNormalized) làm giảm hiệu suất xuống còn khoảng 445K cuộc gọi mỗi giây, tôi có thể sống chung với nó. Tôi vẫn không chắc nó làm được mọi thứ tôi cần - ví dụ: tên tháng chứa "ß" phải khớp với "ss" trong nhiều nền văn hóa, tôi tin rằng ... và việc chuẩn hóa không làm được điều đó.


Mặc dù tôi hiểu mong muốn của bạn là tránh ảnh hưởng hiệu suất khi tạo chuỗi con, nhưng tốt nhất là bạn nên làm như vậy, nhưng trước đó trong trò chơi bằng cách chuyển mọi thứ sang dạng chuẩn hóa unicode đã chọn ĐẦU TIÊN và sau đó biết rằng bạn có thể thực hiện "từng điểm một ". Có lẽ là dạng D.
IDisposable

@IDisposable: Vâng, tôi đã tự hỏi về điều đó. Rõ ràng là tôi có thể bình thường hóa các tên tháng từ trước. Ít nhất tôi có thể thực hiện chuẩn hóa chỉ một lần. Tôi tự hỏi nếu thủ tục bình thường hóa kiểm tra xem có cần phải làm gì trước không. Tôi không có nhiều kinh nghiệm trong việc chuẩn hóa - chắc chắn là một cách để xem xét.
Jon Skeet

1
Nếu textkhông quá dài, bạn có thể làm if (compareInfo.IndexOf(text, candidate, position, options) == position). msdn.microsoft.com/en-us/library/ms143031.aspx Nhưng nếu textquá dài sẽ lãng phí rất nhiều thời gian để tìm kiếm ngoài nơi cần thiết.
Jim Mischel

1
Chỉ cần bỏ qua bằng cách sử dụng Stringlớp ở tất cả trong trường hợp này và sử dụng một Char[]cách trực tiếp. Bạn sẽ kết thúc việc viết mã hơn, nhưng đó là những gì sẽ xảy ra khi bạn muốn hiệu suất cao ... hoặc có lẽ bạn nên được lập trình trong C ++ / CLI ;-)
intrepidis

1
Liệu CompareOptions.IgnoreNonSpace sẽ không tự động xử lý việc này cho bạn chứ? Có vẻ với tôi (từ docco, không phải trong một vị trí để kiểm tra từ iPad này xin lỗi!) Như mặc dù điều này có thể là một ( những ?) Use-case cho tùy chọn đó. " Cho biết rằng so sánh chuỗi phải bỏ qua các ký tự kết hợp không có khoảng cách, chẳng hạn như dấu phụ. "
Sepster

Câu trả lời:


41

Trước tiên, tôi sẽ xem xét vấn đề của nhiều <-> một / nhiều phân tầng và riêng biệt với việc xử lý các dạng Chuẩn hóa khác nhau.

Ví dụ:

x heiße y
  ^--- cursor

Khớp heissenhưng sau đó di chuyển con trỏ 1 quá nhiều. Và:

x heisse y
  ^--- cursor

Khớp heißenhưng sau đó di chuyển con trỏ quá 1 ít.

Điều này sẽ áp dụng cho bất kỳ ký tự nào không có ánh xạ một-một đơn giản.

Bạn sẽ cần biết độ dài của chuỗi con thực sự được khớp. Nhưng Compare, IndexOf.. vv hãy vứt bỏ thông tin đó đi. Nó có thể có với các biểu thức chính quy nhưng việc triển khai không thực hiện gấp chữ hoa chữ thường và do đó không phù hợp ßvới ss/SStrong chế độ phân biệt chữ hoa chữ thường mặc dù .Compare.IndexOfthực hiện. Và có lẽ sẽ rất tốn kém nếu tạo ra các regex mới cho mọi ứng viên.

Giải pháp đơn giản nhất cho điều này là chỉ lưu trữ nội bộ các chuỗi trong trường hợp dạng gấp và thực hiện so sánh nhị phân với các ứng cử viên dạng gấp. Sau đó, bạn có thể di chuyển con trỏ một cách chính xác chỉ .Lengthvì con trỏ là để biểu diễn bên trong. Bạn cũng lấy lại hầu hết hiệu suất bị mất do không phải sử dụng CompareOptions.IgnoreCase.

Thật không may, không có chức năng gấp trường hợp nào được tích hợp và chức năng gấp trường hợp của người đàn ông nghèo cũng không hoạt động vì không có ánh xạ trường hợp đầy đủ - ToUpperphương pháp này không biến ßthành SS.

Ví dụ: điều này hoạt động trong Java (và thậm chí trong Javascript), chuỗi đã cho ở Dạng bình thường C:

//Poor man's case folding.
//There are some edge cases where this doesn't work
public static String toCaseFold( String input, Locale cultureInfo ) {
    return input.toUpperCase(cultureInfo).toLowerCase(cultureInfo);
}

Vui lòng lưu ý rằng so sánh kiểu bỏ qua của Java không thực hiện việc gấp toàn bộ chữ hoa như C # CompareOptions.IgnoreCase. Vì vậy, họ trái ngược nhau về mặt này: Java thực hiện phân tầng đầy đủ, nhưng gấp trường hợp đơn giản - C # thực hiện phân tầng đơn giản, nhưng gấp toàn bộ trường hợp.

Vì vậy, có khả năng bạn cần thư viện của bên thứ 3 để gấp các chuỗi của bạn trước khi sử dụng chúng.


Trước khi làm bất cứ điều gì, bạn phải chắc chắn rằng các chuỗi của bạn ở dạng bình thường C. Bạn có thể sử dụng kiểm tra nhanh sơ bộ này được tối ưu hóa cho chữ viết Latinh:

public static bool MaybeRequiresNormalizationToFormC(string input)
{
    if( input == null ) throw new ArgumentNullException("input");

    int len = input.Length;
    for (int i = 0; i < len; ++i)
    {
        if (input[i] > 0x2FF)
        {
            return true;
        }
    }

    return false;
}

Điều này mang lại kết quả dương tính giả nhưng không phải âm tính giả, tôi không mong đợi nó làm chậm 460k phân tích cú pháp / s khi sử dụng các ký tự chữ viết Latinh mặc dù nó cần được thực hiện trên mọi chuỗi. Với một dương tính giả, bạn sẽ sử dụng IsNormalizedđể nhận được một âm tính / dương tính thực sự và chỉ sau đó bình thường hóa nếu cần thiết.


Vì vậy, kết luận, việc xử lý là để đảm bảo dạng C bình thường trước, sau đó đến trường hợp gấp. Thực hiện so sánh nhị phân với các chuỗi đã xử lý và di chuyển con trỏ khi bạn đang di chuyển nó hiện tại.


Cảm ơn vì điều này - tôi sẽ cần xem xét biểu mẫu chuẩn hóa C chi tiết hơn, nhưng đây là những gợi ý tuyệt vời. Tôi nghĩ rằng tôi có thể sống với "nó không hoạt động hoàn toàn chính xác theo PCL" (không cung cấp chuẩn hóa). Việc sử dụng thư viện của bên thứ 3 để gấp chữ sẽ là quá mức cần thiết ở đây - chúng tôi hiện không có phụ thuộc bên thứ 3 và việc giới thiệu một thư viện chỉ dành cho trường hợp góc mà ngay cả BCL cũng không xử lý được sẽ là một vấn đề. Có lẽ việc xếp chữ hoa chữ thường là nhạy cảm với văn hóa, btw (ví dụ: tiếng Thổ Nhĩ Kỳ)?
Jon Skeet

2
@JonSkeet vâng, Turkic xứng đáng có chế độ riêng trong ánh xạ casefold: P Xem phần định dạng trong tiêu đề của CaseFolding.txt
Esailija

Câu trả lời này dường như có một lỗ hổng cơ bản, trong đó nó ngụ ý rằng các ký tự chỉ liên kết với các chữ ghép (và ngược lại) khi xếp chữ thường. Đây không phải là trường hợp; có những chữ ghép được coi là bình đẳng với các ký tự không phân biệt cách viết hoa. Ví dụ, theo văn hóa en-US, æbằng ae, và bằng ffi. Chuẩn hóa C hoàn toàn không xử lý chữ ghép, vì nó chỉ cho phép ánh xạ tương thích (thường bị hạn chế trong việc kết hợp các ký tự).
Douglas

KC- và KD-chuẩn hóa xử lý một số chữ ghép, chẳng hạn như , nhưng bỏ sót những chữ ghép khác, chẳng hạn như æ. Vấn đề trở nên tồi tệ hơn do sự khác biệt giữa các nền văn hóa - ængang bằng với aeen-US, nhưng không theo da-DK, như được thảo luận trong tài liệu MSDN cho chuỗi . Do đó, chuẩn hóa (ở bất kỳ dạng nào) và ánh xạ trường hợp không phải là một giải pháp đầy đủ cho vấn đề này.
Douglas

Điều chỉnh nhỏ đối với nhận xét trước đây của tôi: Chuẩn hóa C chỉ cho phép ánh xạ chuẩn (chẳng hạn như để kết hợp các ký tự), không phải ánh xạ tương thích (chẳng hạn như cho chữ ghép).
Douglas

21

Xem điều này có đáp ứng yêu cầu không ..:

public static partial class GlobalizationExtensions {
    public static int IsPrefix(
        this CompareInfo compareInfo,
        String source, String prefix, int startIndex, CompareOptions options
        ) {
        if(compareInfo.IndexOf(source, prefix, startIndex, options)!=startIndex)
            return ~0;
        else
            // source is started with prefix
            // therefore the loop must exit
            for(int length2=0, length1=prefix.Length; ; )
                if(0==compareInfo.Compare(
                        prefix, 0, length1, 
                        source, startIndex, ++length2, options))
                    return length2;
    }
}

compareInfo.Comparechỉ thực hiện khi sourcebắt đầu với prefix; nếu nó không, sau đó IsPrefixtrả về -1; nếu không, độ dài của các ký tự được sử dụng trong source.

Tuy nhiên, tôi không có ý tưởng trừ increment length2bằng 1với trường hợp sau đây:

var candidate="ßssß\u00E9\u0302";
var text="abcd ssßss\u0065\u0301\u0302sss";

var count=
    culture.CompareInfo.IsPrefix(text, candidate, 5, CompareOptions.IgnoreCase);

cập nhật :

Tôi đã cố gắng cải thiện một chút hiệu suất, nhưng nó không được chứng minh là có lỗi trong đoạn mã sau hay không:

public static partial class GlobalizationExtensions {
    public static int Compare(
        this CompareInfo compareInfo,
        String source, String prefix, int startIndex, ref int length2, 
        CompareOptions options) {
        int length1=prefix.Length, v2, v1;

        if(0==(v1=compareInfo.Compare(
            prefix, 0, length1, source, startIndex, length2, options))
            ) {
            return 0;
        }
        else {
            if(0==(v2=compareInfo.Compare(
                prefix, 0, length1, source, startIndex, 1+length2, options))
                ) {
                ++length2;
                return 0;
            }
            else {
                if(v1<0||v2<0) {
                    length2-=2;
                    return -1;
                }
                else {
                    length2+=2;
                    return 1;
                }
            }
        }
    }

    public static int IsPrefix(
        this CompareInfo compareInfo,
        String source, String prefix, int startIndex, CompareOptions options
        ) {
        if(compareInfo.IndexOf(source, prefix, startIndex, options)
                !=startIndex)
            return ~0;
        else
            for(int length2=
                    Math.Min(prefix.Length, source.Length-(1+startIndex)); ; )
                if(0==compareInfo.Compare(
                        source, prefix, startIndex, ref length2, options))
                    return length2;
    }
}

Tôi đã thử nghiệm với trường hợp cụ thể và so sánh giảm xuống còn khoảng 3.


Tôi thực sự không muốn phải lặp lại như thế này. Phải thừa nhận rằng ngay từ đầu, nó sẽ chỉ cần lặp lại nếu tìm thấy thứ gì đó, nhưng tôi vẫn không muốn phải thực hiện 8 phép so sánh chuỗi chỉ để khớp với "Tháng Hai" chẳng hạn. Có vẻ như phải có một cách tốt hơn. Ngoài ra, IndexOfhoạt động ban đầu phải xem qua toàn bộ chuỗi từ vị trí bắt đầu, điều này sẽ gây khó khăn cho hiệu suất nếu chuỗi đầu vào dài.
Jon Skeet

@JonSkeet: Cảm ơn bạn. Có thể có một cái gì đó có thể được thêm vào để phát hiện xem vòng lặp có thể được giảm. Tôi sẽ nghĩ về điều đó.
Ken Kin

@JonSkeet: Bạn có cân nhắc sử dụng phản xạ không? Kể từ khi tôi truy tìm các phương thức, chúng rơi vào việc gọi các phương thức gốc không xa.
Ken Kin

3
Thật. Noda Time không muốn tham gia vào công việc kinh doanh của chi tiết Unicode :)
Jon Skeet

2
Tôi đã giải quyết một vấn đề tương tự một lần như thế này (đánh dấu chuỗi tìm kiếm trong HTML). Tôi đã làm điều đó tương tự. Bạn có thể điều chỉnh vòng lặp và chiến lược tìm kiếm theo cách làm cho nó được hoàn thành rất nhanh bằng cách kiểm tra các trường hợp có thể xảy ra trước. Điều tốt đẹp về điều này là nó có vẻ hoàn toàn chính xác và không có chi tiết Unicode nào bị rò rỉ vào mã của bạn.
usr

9

Điều này thực sự có thể thực hiện mà không cần chuẩn hóa và không cần sử dụng IsPrefix.

Chúng ta cần so sánh cùng một số phần tử văn bản trái ngược với cùng một số ký tự, nhưng vẫn trả về số ký tự phù hợp.

Tôi đã tạo một bản sao của MatchCaseInsensitivephương thức từ ValueCursor.cs trong Noda Time và sửa đổi nó một chút để nó có thể được sử dụng trong ngữ cảnh tĩnh:

// Noda time code from MatchCaseInsensitive in ValueCursor.cs
static int IsMatch_Original(string source, int index, string match, CompareInfo compareInfo)
{
    unchecked
    {
        if (match.Length > source.Length - index)
        {
            return 0;
        }

        // TODO(V1.2): This will fail if the length in the input string is different to the length in the
        // match string for culture-specific reasons. It's not clear how to handle that...
        if (compareInfo.Compare(source, index, match.Length, match, 0, match.Length, CompareOptions.IgnoreCase) == 0)
        {
            return match.Length;
        }

        return 0;
    }
}

(Chỉ được đưa vào để tham khảo, đây là mã sẽ không so sánh đúng như bạn biết)

Biến thể sau của phương thức đó sử dụng StringInfo.GetNextTextElement được cung cấp bởi khung. Ý tưởng là so sánh phần tử văn bản với phần tử văn bản để tìm một kết quả phù hợp và nếu tìm thấy sẽ trả về số lượng ký tự phù hợp thực tế trong chuỗi nguồn:

// Using StringInfo.GetNextTextElement to match by text elements instead of characters
static int IsMatch_New(string source, int index, string match, CompareInfo compareInfo)
{
    int sourceIndex = index;
    int matchIndex = 0;

    // Loop until we reach the end of source or match
    while (sourceIndex < source.Length && matchIndex < match.Length)
    {
        // Get text elements at the current positions of source and match
        // Normally that will be just one character but may be more in case of Unicode combining characters
        string sourceElem = StringInfo.GetNextTextElement(source, sourceIndex);
        string matchElem = StringInfo.GetNextTextElement(match, matchIndex);

        // Compare the current elements.
        if (compareInfo.Compare(sourceElem, matchElem, CompareOptions.IgnoreCase) != 0)
        {
            return 0; // No match
        }

        // Advance in source and match (by number of characters)
        sourceIndex += sourceElem.Length;
        matchIndex += matchElem.Length;
    }

    // Check if we reached end of source and not end of match
    if (matchIndex != match.Length)
    {
        return 0; // No match
    }

    // Found match. Return number of matching characters from source.
    return sourceIndex - index;
}

Phương pháp đó hoạt động tốt ít nhất là theo các trường hợp thử nghiệm của tôi (về cơ bản chỉ kiểm tra một vài biến thể của chuỗi bạn đã cung cấp: "b\u00e9d""be\u0301d").

Tuy nhiên, phương thức GetNextTextElement tạo một chuỗi con cho mỗi phần tử văn bản, do đó, việc triển khai này yêu cầu rất nhiều so sánh chuỗi con - điều này sẽ có tác động đến hiệu suất.

Vì vậy, tôi đã tạo một biến thể khác không sử dụng GetNextTextElement mà bỏ qua các ký tự kết hợp Unicode để tìm độ dài khớp thực tế trong các ký tự:

// This should be faster
static int IsMatch_Faster(string source, int index, string match, CompareInfo compareInfo)
{
    int sourceLength = source.Length;
    int matchLength = match.Length;
    int sourceIndex = index;
    int matchIndex = 0;

    // Loop until we reach the end of source or match
    while (sourceIndex < sourceLength && matchIndex < matchLength)
    {
        sourceIndex += GetTextElemLen(source, sourceIndex, sourceLength);
        matchIndex += GetTextElemLen(match, matchIndex, matchLength);
    }

    // Check if we reached end of source and not end of match
    if (matchIndex != matchLength)
    {
        return 0; // No match
    }

    // Check if we've found a match
    if (compareInfo.Compare(source, index, sourceIndex - index, match, 0, matchIndex, CompareOptions.IgnoreCase) != 0)
    {
        return 0; // No match
    }

    // Found match. Return number of matching characters from source.
    return sourceIndex - index;
}

Phương pháp đó sử dụng hai trợ giúp sau:

static int GetTextElemLen(string str, int index, int strLen)
{
    bool stop = false;
    int elemLen;

    for (elemLen = 0; index < strLen && !stop; ++elemLen, ++index)
    {
        stop = !IsCombiningCharacter(str, index);
    }

    return elemLen;
}

static bool IsCombiningCharacter(string str, int index)
{
    switch (CharUnicodeInfo.GetUnicodeCategory(str, index))
    {
        case UnicodeCategory.NonSpacingMark:
        case UnicodeCategory.SpacingCombiningMark:
        case UnicodeCategory.EnclosingMark:
            return true;

        default:
            return false;
    }
}

Tôi chưa thực hiện bất kỳ đánh dấu băng ghế nào, vì vậy tôi không thực sự biết liệu phương pháp nhanh hơn có thực sự nhanh hơn hay không. Tôi cũng chưa thực hiện bất kỳ thử nghiệm mở rộng nào.

Nhưng điều này sẽ trả lời câu hỏi của bạn về cách thực hiện đối sánh chuỗi con nhạy cảm về văn hóa cho các chuỗi có thể bao gồm các ký tự kết hợp Unicode.

Đây là những trường hợp thử nghiệm tôi đã sử dụng:

static Tuple<string, int, string, int>[] tests = new []
{
    Tuple.Create("x b\u00e9d y", 2, "be\u0301d", 3),
    Tuple.Create("x be\u0301d y", 2, "b\u00e9d", 4),

    Tuple.Create("x b\u00e9d", 2, "be\u0301d", 3),
    Tuple.Create("x be\u0301d", 2, "b\u00e9d", 4),

    Tuple.Create("b\u00e9d y", 0, "be\u0301d", 3),
    Tuple.Create("be\u0301d y", 0, "b\u00e9d", 4),

    Tuple.Create("b\u00e9d", 0, "be\u0301d", 3),
    Tuple.Create("be\u0301d", 0, "b\u00e9d", 4),

    Tuple.Create("b\u00e9", 0, "be\u0301d", 0),
    Tuple.Create("be\u0301", 0, "b\u00e9d", 0),
};

Các giá trị tuple là:

  1. Chuỗi nguồn (haystack)
  2. Vị trí bắt đầu trong nguồn.
  3. Dây diêm (kim).
  4. Thời lượng khớp dự kiến.

Chạy các thử nghiệm đó trên ba phương pháp sẽ mang lại kết quả sau:

Test #0: Orignal=BAD; New=OK; Faster=OK
Test #1: Orignal=BAD; New=OK; Faster=OK
Test #2: Orignal=BAD; New=OK; Faster=OK
Test #3: Orignal=BAD; New=OK; Faster=OK
Test #4: Orignal=BAD; New=OK; Faster=OK
Test #5: Orignal=BAD; New=OK; Faster=OK
Test #6: Orignal=BAD; New=OK; Faster=OK
Test #7: Orignal=BAD; New=OK; Faster=OK
Test #8: Orignal=OK; New=OK; Faster=OK
Test #9: Orignal=OK; New=OK; Faster=OK

Hai thử nghiệm cuối cùng đang kiểm tra trường hợp khi chuỗi nguồn ngắn hơn chuỗi khớp. Trong trường hợp này, phương pháp ban đầu (Noda time) cũng sẽ thành công.


Cám ơn bạn rất nhiều về điều này. Tôi sẽ cần xem xét chi tiết nó để xem nó hoạt động tốt như thế nào, nhưng nó có vẻ như là một điểm khởi đầu tuyệt vời. Nhiều kiến thức về Unicode (trong mã chính nó) hơn những gì tôi hy vọng sẽ được yêu cầu, nhưng nếu nền tảng này không làm những gì cần thiết, không có rất nhiều tôi có thể làm gì về :( rằng
Jon Skeet

@JonSkeet: Rất vui khi được giúp đỡ! Và có, phù hợp với substring với sự hỗ trợ Unicode nên chắc chắn đã được đưa vào khuôn khổ ...
Marten Wikström
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.