Chữ hoa trên và chữ thường


85

Khi thực hiện so sánh không phân biệt chữ hoa chữ thường, chuyển đổi chuỗi thành chữ hoa hoặc chữ thường có hiệu quả hơn không? Nó thậm chí không quan trọng?

Trong bài đăng SO này được gợi ý rằng C # hiệu quả hơn với ToUpper vì "Microsoft đã tối ưu hóa nó theo cách đó." Nhưng tôi cũng đã đọc lập luận này rằng việc chuyển đổi ToLower so với ToUpper phụ thuộc vào những gì chuỗi của bạn chứa nhiều hơn và các chuỗi thường chứa nhiều ký tự chữ thường hơn, điều này làm cho ToLower hiệu quả hơn.

Đặc biệt, tôi muốn biết:

  • Có cách nào để tối ưu hóa ToUpper hoặc ToLower để cái này nhanh hơn cái kia không?
  • So sánh phân biệt chữ hoa chữ thường giữa các chuỗi chữ hoa hoặc chữ thường có nhanh hơn không và tại sao?
  • Có bất kỳ môi trường lập trình nào (ví dụ: C, C #, Python, bất cứ thứ gì) trong đó một trường hợp rõ ràng là tốt hơn trường hợp kia không, và tại sao?

Câu trả lời:


90

Việc chuyển đổi thành chữ hoa hoặc chữ thường để so sánh không phân biệt chữ hoa chữ thường là không chính xác do các đặc điểm "thú vị" của một số nền văn hóa, đặc biệt là Thổ Nhĩ Kỳ. Thay vào đó, hãy sử dụng StringComparer với các tùy chọn thích hợp.

MSDN có một số hướng dẫn tuyệt vời về xử lý chuỗi. Bạn cũng có thể muốn kiểm tra xem mã của mình có vượt qua bài kiểm tra của Thổ Nhĩ Kỳ hay không .

CHỈNH SỬA: Lưu ý nhận xét của Neil về các phép so sánh không phân biệt chữ hoa chữ thường theo thứ tự . Cả cõi này khá âm u :(


15
Có StringComparer rất tuyệt, nhưng câu hỏi không được trả lời ... Trong những trường hợp bạn không thể sử dụng StringComparer chẳng hạn như câu lệnh swtich đối với một chuỗi; tôi nên ToUpper hoặc ToLower trong công tắc?
joshperry 22/02/09

7
Sử dụng StringComparer và "if" / "else" thay vì sử dụng ToUpper hoặc ToLower.
Jon Skeet

5
John, tôi biết rằng chuyển đổi thành chữ thường là không chính xác, nhưng tôi đã không nghe nói rằng chuyển đổi thành chữ hoa là không chính xác. Bạn có thể cung cấp một ví dụ hoặc một tài liệu tham khảo? Bài báo MSDN mà bạn đã liên kết cho biết điều này: "Các phép so sánh được thực hiện bằng OrdinalIgnoreCase về mặt hành vi là thành phần của hai lệnh gọi: gọi ToUpperInvariant trên cả hai đối số chuỗi và thực hiện so sánh Ordinal." Trong phần có tiêu đề "Hoạt động chuỗi thứ tự", nó trình bày lại điều này trong mã.
Neil

2
@Neil: Thật thú vị, tôi đã không nhìn thấy nó. Đối với một so sánh không phân biệt chữ hoa chữ thường theo thứ tự , tôi đoán điều đó đủ công bằng. Rốt cuộc thì nó phải chọn thứ gì đó . Đối với các phép so sánh không phân biệt chữ hoa chữ thường về văn hóa, tôi nghĩ vẫn còn chỗ cho một số hành vi kỳ quặc. Sẽ chỉ ra bình luận của bạn trong câu trả lời ...
Jon Skeet

4
@Triynko: Tôi nghĩ điều quan trọng là phải tập trung chủ yếu vào tính đúng đắn, với quan điểm là nhận được câu trả lời sai nhanh thường không tốt hơn (và đôi khi còn tệ hơn) so với việc trả lời sai một cách chậm chạp.
Jon Skeet

25

Từ Microsoft trên MSDN:

Các phương pháp hay nhất để sử dụng chuỗi trong .NET Framework

Khuyến nghị cho việc sử dụng chuỗi

Tại sao? Của Microsoft :

Chuẩn hóa chuỗi thành chữ hoa

Có một nhóm nhỏ các ký tự khi chuyển đổi thành chữ thường không thể thực hiện một chuyến đi vòng.

Ví dụ về một nhân vật không thể thực hiện một chuyến đi khứ hồi là gì?

  • Bắt đầu : Biểu tượng Rho Hy Lạp (U + 03f1) ϱ
  • Chữ hoa: Chữ Hy Lạp viết hoa Rho (U + 03a1) Ρ
  • Chữ thường: Rho Hy Lạp nhỏ (U + 03c1) ρ

ϱ, Ρ , ρ

.NET Fiddle

Original: ϱ
ToUpper: Ρ
ToLower: ρ

Đó là lý do tại sao, nếu bạn muốn so sánh không phân biệt chữ hoa chữ thường, bạn chuyển đổi các chuỗi thành chữ hoa chứ không phải chữ thường.

Vì vậy, nếu bạn phải chọn một, hãy chọn Chữ hoa .


và lý do là gì?
bjan

@bjan Lý do là vì nó không tốt.
Ian Boyd

1
Nhóm nhân vật nào? Làm cho một chuyến đi khứ hồi có nghĩa là gì?
johv

1
@johv Từ liên kết: "Để thực hiện một chuyến đi vòng có nghĩa là chuyển đổi các ký tự từ ngôn ngữ này sang ngôn ngữ khác đại diện cho dữ liệu ký tự khác nhau và sau đó truy xuất chính xác các ký tự gốc từ các ký tự đã chuyển đổi." Nhóm nhân vật nào? Tôi không biết, nhưng tôi sẽ đoán chữ thường itrong tiếng Thổ Nhĩ Kỳ, khi trở thành İ, thay vì Inhư bạn đã quen. Ngoài ra, chúng tôi đã quen với việc viết hoa Itrở thành i, nhưng ở Thổ Nhĩ Kỳ thì nó trở thành ı.
Ian Boyd

3
Quay lại câu trả lời cho câu hỏi ban đầu: Có những ngôn ngữ biết nhiều hơn một biến thể viết thường cho một biến thể viết hoa. Trừ khi bạn biết các quy tắc về thời điểm sử dụng biểu diễn nào (một ví dụ khác bằng tiếng Hy Lạp: chữ cái sigma nhỏ, bạn sử dụng σ ở đầu từ hoặc ở giữa, ς ở cuối từ (xem en.wikipedia.org/wiki/Sigma ), bạn có thể không an toàn chuyển đổi trở lại trường hợp biến thể thấp hơn.
Aconcagua

19

Theo MSDN , hiệu quả hơn là chuyển vào các chuỗi và yêu cầu so sánh bỏ qua trường hợp:

String.Compare (strA, strB, StringComparison.OrdinalIgnoreCase) tương đương với ( nhưng nhanh hơn ) gọi

String.Compare (ToUpperInvariant (strA), ToUpperInvariant (strB), StringComparison.Ordinal).

Những so sánh này vẫn còn rất nhanh.

Tất nhiên, nếu bạn đang so sánh lặp đi lặp lại một chuỗi thì điều này có thể không ổn.


12

Dựa trên các chuỗi có xu hướng có nhiều mục nhập chữ thường hơn, về mặt lý thuyết, ToLower sẽ nhanh hơn (nhiều phép so sánh, nhưng ít phép gán).

Trong C, hoặc khi sử dụng các phần tử có thể truy cập riêng lẻ của từng chuỗi (chẳng hạn như chuỗi C hoặc kiểu chuỗi của STL trong C ++), nó thực sự là một phép so sánh byte - vì vậy so sánh UPPERkhông khác gì lower.

Nếu bạn lén lút và tải các chuỗi của mình vào longcác mảng thay vào đó, bạn sẽ nhận được so sánh rất nhanh trên toàn bộ chuỗi vì nó có thể so sánh 4 byte cùng một lúc. Tuy nhiên, thời gian tải có thể làm cho nó không đáng giá.

Tại sao bạn cần biết cái nào nhanh hơn? Trừ khi bạn đang thực hiện một loạt các phép so sánh chỉ số, việc chạy nhanh hơn một vài chu kỳ không liên quan đến tốc độ thực thi tổng thể và nghe có vẻ như là tối ưu hóa quá sớm :)


11
Để trả lời câu hỏi tại sao tôi cần biết cái nào nhanh hơn: Tôi không cần biết, tôi chỉ muốn biết. :) Nó chỉ đơn giản là trường hợp thấy ai đó đưa ra yêu cầu (chẳng hạn như "so sánh các chuỗi chữ hoa nhanh hơn!") Và muốn biết liệu điều đó có thực sự đúng hay không và / hoặc tại sao họ đưa ra yêu cầu đó.
Parappa 24/10/08

1
điều đó có ý nghĩa - tôi cũng luôn tò mò về những thứ như thế này :)
warren

Với chuỗi C, để chuyển đổi stthành các mảng có độ dài sao cho các chuỗi bằng nhau, các mảng bằng nhau, bạn phải đi xuống s và t cho đến khi bạn tìm thấy '\0'ký tự kết thúc (hoặc nếu không, bạn có thể so sánh rác ở cuối chuỗi, có thể là một truy cập bộ nhớ bất hợp pháp gây ra hành vi không xác định). Nhưng tại sao không thực hiện các phép so sánh trong khi xem xét từng nhân vật một? Với chuỗi C ++, bạn có thể lấy độ dài và .c_str(), ép kiểu thành a long *và so sánh tiền tố độ dài .size() - .size()%(sizeof long). Trông hơi tanh đối với tôi, tho.
Jonas Kölker

6

Microsoft đã tối ưu hóa ToUpperInvariant(), không ToUpper(). Sự khác biệt là bất biến thân thiện với văn hóa hơn. Nếu bạn cần thực hiện so sánh phân biệt chữ hoa chữ thường trên các chuỗi có thể khác nhau về văn hóa, hãy sử dụng Bất biến, nếu không thì hiệu suất của chuyển đổi bất biến sẽ không thành vấn đề.

Tuy nhiên, tôi không thể nói liệu ToUpper () hay ToLower () nhanh hơn. Tôi chưa bao giờ thử nó vì tôi chưa bao giờ gặp tình huống mà hiệu suất lại quan trọng đến thế.


nếu Microsoft đã tối ưu hóa mã để thực hiện so sánh chữ hoa có phải là vì mã ASCII cho chữ hoa chỉ có hai chữ số 65-90 trong khi mã ASCII Chữ thường 97 -122 chứa 3 chữ số (cần xử lý thêm)?
Medo Medo

3
@Medo Tôi không nhớ chính xác lý do tối ưu hóa, nhưng chữ số 2 vs 3 gần như chắc chắn không phải là lý do vì tất cả các chữ cái được lưu trữ dưới dạng số nhị phân, vì vậy chữ số thập phân không thực sự có ý nghĩa dựa trên cách chúng được lưu trữ.
Dan Herbert

4

Nếu bạn đang thực hiện so sánh chuỗi trong C # thì sẽ nhanh hơn đáng kể khi sử dụng .Equals () thay vì chuyển đổi cả hai chuỗi thành chữ hoa hoặc chữ thường. Một điểm cộng lớn khác khi sử dụng .Equals () là nhiều bộ nhớ hơn không được phân bổ cho 2 chuỗi chữ hoa / thường mới.


4
Và như một phần thưởng, nếu bạn chọn tùy chọn đúng nó sẽ thực sự cung cấp cho bạn những kết quả đúng :)
Jon Skeet

1

Nó thực sự không bao giờ nên quan trọng. Với các ký tự ASCII, điều đó chắc chắn không quan trọng - đó chỉ là một vài so sánh và một chút lật ngược cho cả hai hướng. Unicode có thể phức tạp hơn một chút, vì có một số ký tự thay đổi chữ hoa chữ thường theo những cách kỳ lạ, nhưng thực sự sẽ không có bất kỳ sự khác biệt nào trừ khi văn bản của bạn chứa đầy các ký tự đặc biệt đó.


1

Làm đúng, sẽ có một lợi thế tốc độ nhỏ, không đáng kể nếu bạn chuyển đổi sang chữ thường, nhưng điều này, như nhiều người đã gợi ý, phụ thuộc vào văn hóa và không được kế thừa trong hàm nhưng trong chuỗi bạn chuyển đổi (rất nhiều chữ thường nghĩa là có ít phép gán cho bộ nhớ) - chuyển đổi sang chữ hoa sẽ nhanh hơn nếu bạn có một chuỗi có nhiều chữ hoa.


0

Nó phụ thuộc. Như đã nêu ở trên, đơn giản chỉ là ASCII, giống hệt của nó. Trong .NET, hãy đọc về và sử dụng String. So sánh chính xác của nó với nội dung i18n (ngôn ngữ văn hóa và unicode). Nếu bạn biết bất cứ điều gì về khả năng của đầu vào, hãy sử dụng trường hợp phổ biến hơn.

Hãy nhớ rằng, nếu bạn đang thực hiện nhiều chuỗi so sánh độ dài là một phân biệt đầu tiên tuyệt vời.


-2

Nếu bạn đang xử lý ASCII thuần túy, điều đó không thành vấn đề. Nó chỉ là OR x, 32 so với AND x, 224. Unicode, tôi không biết ...


4
Điều này hoàn toàn sai - OR'ing với 32 chỉ hoạt động cho AZ và các ký tự 64-127; nó bắt vít tất cả các ký tự khác. AND'ing với 32 thậm chí còn sai hơn - kết quả sẽ luôn là 0 (nul) hoặc 32 (khoảng trắng).
Adam Rosenfield 24/10/08
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.