2012-11-20 35 views
5

Tôi có một chuỗi các chuỗi được sắp xếp. Cho một chuỗi xác định một tiền tố, tôi thực hiện hai tìm kiếm nhị phân để tìm ra vị trí đầu tiên và cuối cùng trong mảng đó chứa các từ bắt đầu bằng tiền tố rằng:Tại sao ("abc" + char.MaxValue) .CompareTo ("abc") == 0?

string [] words = {"aaa","abc","abcd","acd"}; 
string prefix = "abc"; 
int firstPosition = Array.BinarySearch<string>(words, prefix); 
int lastPosition = Array.BinarySearch<string>(words, prefix + char.MaxValue); 
if (firstPosition < 0) 
    firstPosition = ~firstPosition; 
if (lastPosition < 0) 
    lastPosition = ~lastPosition; 

Chạy mã này tôi nhận được firstPosition và lastPosition cả bình đẳng đến 1, trong khi câu trả lời đúng là có lastPosition bằng 3 (tức là, trỏ đến từ không phù hợp đầu tiên). Phương pháp BinarySearch sử dụng phương pháp CompareTo để so sánh các đối tượng và tôi đã phát hiện ra rằng

("abc"+char.MaxValue).CompareTo("abc")==0 

nghĩa rằng hai chuỗi được coi là bình đẳng! Nếu tôi thay đổi mã bằng

int lastPosition = Array.BinarySearch<string>(words, prefix + "z"); 

Tôi nhận được câu trả lời đúng. Hơn nữa, tôi thấy rằng

("abc"+char.MaxValue)==("abc") 

chính xác (theo nhu cầu của tôi) trả về false.

Bạn có thể vui lòng giúp tôi giải thích hành vi của phương thức CompareTo không?

Tôi muốn có phương thức CompareTo hoạt động như ==, do đó phương thức BinarySearch trả về 3 cho lastPosition.

+4

Bất kể 'Tìm kiếm nhị phân' sử dụng ở đây (fe' string.IndexOf' hoặc 'CompareTo'). Nó bỏ qua giá trị hex của 'Char.MaxValue'. Tìm kiếm 'ignore' trong [** Jon Skeets blog **] (http://csharpindepth.com/Articles/General/strings.aspx) và bạn thấy rằng nó có thể. Từ [MSDN] (http://msdn.microsoft.com/en-us/library/35f0x18w.aspx): _ "Phương thức CompareTo được thiết kế để sử dụng trong các hoạt động sắp xếp hoặc sắp xếp bảng chữ cái. Nó không nên được sử dụng khi mục đích chính của cuộc gọi phương thức là xác định xem hai chuỗi có tương đương hay không. "_ –

+0

Đoạn mã này là một phần của chức năng mục đích chung hơn thực hiện tìm kiếm" phạm vi "trên các kiểu dữ liệu khác nhau, miễn là đối tượng tìm kiếm triển khai IComparable. Vì lý do này, mặc dù tìm kiếm nhị phân là một giải pháp linh hoạt, trong khi sử dụng các cấu trúc dữ liệu văn bản dành riêng như cây tiền tố không phù hợp với mục đích. Tôi nghĩ rằng một giải pháp có thể tiết kiệm các vấn đề linh hoạt là gói gọn loại chuỗi thành một loại thực hiện so sánh "đúng" trong phương thức CompareTo của nó. – Esuli

Trả lời

5

Theo MSDN, string.CompareTo không nên được sử dụng để kiểm tra xem hai chuỗi bằng nhau:

Phương pháp CompareTo được thiết kế chủ yếu để sử dụng trong phân loại hoặc alphabetizing hoạt động. Nó không nên được sử dụng khi mục đích chính của cuộc gọi phương thức là xác định xem hai chuỗi có tương đương hay không. Để xác định xem hai chuỗi có tương đương hay không, hãy gọi phương thức Equals.

Để có được hành vi mà bạn muốn, bạn có thể tận dụng sự quá tải mà chấp nhận một IComparer<T>:

int lastPosition = Array.BinarySearch<string>(words, prefix + char.MaxValue, 
               StringComparer.Ordinal); 

này sẽ trở lại -4 cho lastPosition như không có chuỗi với tiền tố trong mảng. Tôi không hiểu tại sao bạn mong đợi 3 trong trường hợp đó ...

+0

Vấn đề là nó không phải là mã của tôi gọi phương thức CompareTo, nó là phương thức Array .BinarySearch. – Esuli

+0

@Esuli: bạn có thể thay đổi điều đó. Vui lòng xem cập nhật. –

+0

'if (lastPosition <0) lastPosition = ~ lastPosition;' chuyển đổi -4 thành 3. StringComparer.Ordinal là trình so sánh đúng. Bây giờ tôi phải quyết định cách cắm nó vào mã thực mà không phá vỡ tính linh hoạt chung. – Esuli

6

string.CompareTo() so sánh văn hóa hiện tại. Bên trong nó sử dụng StringComparer.CurrentCulture, trong khi chuỗi bằng toán tử thực hiện so sánh văn hóa bất biến.

Ví dụ, nếu dòng điện-văn hóa là "DE", bạn sẽ nhận được kết quả tương tự với "ss" và "ß":

Console.WriteLine("ss".CompareTo("ß")); // => 0 
Console.WriteLine("ss" == "ß"); // => false 

gì bạn muốn là một nền văn hóa bất biến so sánh, mà bạn sẽ nhận được bằng cách sử dụng StringComparer.Ordinal:

StringComparer.Ordinal.Compare("ss", "ß"); // => -108 
StringComparer.Ordinal.Compare("abc"+char.MaxValue, "abc"); // => 65535 
+0

Câu trả lời Godd. Như một bổ sung: OP có thể muốn sử dụng 'StringComparer.OrdinalIgnoreCase.Compare (..., ...)' hoặc 'String.Compare (..., ..., StringComparison.OrdinalIgnoreCase)' để đạt được một nền văn hóa bất biến , so sánh không phân biệt dạng chữ. –

+0

Cảm ơn bạn đã cung cấp thông tin hữu ích. – Esuli

+0

@Abbondanza Chính xác. Và cũng có 'string.CompareOrdinal (" abc \ uFFFF "," abc ")'. Nhưng vì người hỏi gọi phương thức 'BinarySearch', anh ta nên chuyển thể loại' IComparer 'được gọi là' StringComparer.Ordinal'. –

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