Büyük ve Küçük Harf


86

Büyük / küçük harfe duyarlı olmayan karşılaştırmalar yaparken, dizeyi büyük harfe veya küçük harfe dönüştürmek daha mı verimli? Fark eder mi?

Bu SO gönderisinde , "Microsoft bunu bu şekilde optimize ettiği için C # 'ın ToUpper ile daha verimli olduğu öneriliyor . Ancak ToLower'ı ToUpper'a dönüştürmenin dizelerinizin daha fazla neyi içerdiğine bağlı olduğunu ve tipik olarak dizelerin daha küçük harf karakterleri içerdiğinden ToLower'ı daha verimli kıldığına dair bu argümanı da okudum .

Özellikle şunu bilmek isterim:

  • ToUpper veya ToLower'ı biri diğerinden daha hızlı olacak şekilde optimize etmenin bir yolu var mı?
  • Büyük veya küçük harfli dizeler arasında büyük / küçük harfe duyarlı olmayan bir karşılaştırma yapmak daha mı hızlıdır ve neden?
  • Bir vakanın diğerinden açıkça daha iyi olduğu herhangi bir programlama ortamı (örn. C, C #, Python, her neyse) var mı ve neden?

Yanıtlar:


90

Büyük / küçük harfe çevirmek, büyük / küçük harfe duyarlı olmayan karşılaştırmalar yapabilmek için bazı kültürlerin, özellikle de Türkiye'nin "ilginç" özelliklerinden dolayı yanlıştır. Bunun yerine, uygun seçeneklerle bir StringComparer kullanın .

MSDN, dizi işleme konusunda bazı harika yönergelere sahiptir . Kodunuzun Türkiye testini geçip geçmediğini de kontrol etmek isteyebilirsiniz .

DÜZENLEME: Neil'in sıralı büyük / küçük harfe duyarlı olmayan karşılaştırmalar hakkındaki yorumuna dikkat edin . Bütün bu bölge oldukça karanlık :(


15
Evet StringComparer harika, ancak soru yanıtlanmadı ... StringComparer'ı kullanamadığınız durumlarda, örneğin bir dizgiye karşı swtich ifadesi; Anahtarda ToUpper veya ToLower yapmalı mıyım?
joshperry

7
ToUpper veya ToLower kullanmak yerine bir StringComparer ve "if" / "else" kullanın.
Jon Skeet

5
John, küçük harfe dönüştürmenin yanlış olduğunu biliyorum, ancak büyük harfe dönüştürmenin yanlış olduğunu duymamıştım. Bir örnek veya referans verebilir misiniz? Bağlandığınız MSDN makalesi şunu söylüyor: "OrdinalIgnoreCase kullanılarak yapılan karşılaştırmalar davranışsal olarak iki çağrının bileşimidir: her iki dize bağımsız değişkeninde ToUpperInvariant'ı çağırma ve bir Sıralı karşılaştırma yapma." "Sıra Dizgisi İşlemleri" başlıklı bölümde bunu kodda yeniden ifade etmektedir.
Neil

2
@Neil: İlginç, o kadarını görmemiştim. Bir İçin sıralı harf duyarsız karşılaştırma, bunun adil yeter sanırım. Ne de olsa bir şey seçmeli . Kültürel açıdan hassas, büyük / küçük harfe duyarsız karşılaştırmalar için, bazı garip davranışlar için hala yer olacağını düşünüyorum. Cevabınızda yorumunuzu gösterecek ...
Jon Skeet

4
@Triynko: Bence öncelikle doğruluğa konsantre olmanın önemli olduğunu düşünüyorum , çünkü yanlış yanıtı hızlı bir şekilde almak genellikle yanlış yanıtı yavaşça almaktan daha iyi değildir (ve bazen daha kötüdür).
Jon Skeet

25

Gönderen Microsoft MSDN üzerinde:

.NET Framework'te Dizeleri Kullanmaya Yönelik En İyi Uygulamalar

Dize Kullanımı için Öneriler

Neden? Gönderen Microsoft :

Dizeleri büyük harfe normalleştirme

Küçük harfe dönüştürüldüğünde gidiş-dönüş yapamayan küçük bir karakter grubu vardır.

Böyle gidiş-dönüş yapamayan bir karakterin örneği nedir?

  • Başlangıç : Yunan Rho Sembolü (U + 03f1) ϱ
  • Büyük harf: Capital Greek Rho (U + 03a1) Ρ
  • Küçük harf: Küçük Yunan Rho (U + 03c1) ρ

ϱ, Ρ , ρ

.NET Fiddle

Original: ϱ
ToUpper: Ρ
ToLower: ρ

Bu nedenle, büyük / küçük harfe duyarlı olmayan karşılaştırmalar yapmak istiyorsanız dizeleri küçük harfe değil büyük harfe dönüştürürsünüz.

Yani birini seçmeniz gerekiyorsa Büyük Harf'i seçin .


3
Cevaba dönersek orijinal soru: Bir büyük harf varyantı için birden fazla küçük harf varyantı bilen diller vardır. Hangi temsilini (Yunanca başka bir örnek ne zaman kullanılacağına ilişkin kuralları bilmek sürece: Küçük sigma mektup, sen kelime sonundaki ς (bkz kelime başında veya ortasında σ kullanmak en.wikipedia.org/wiki/Sigma ), güvenli bir şekilde küçük harf varyantına dönüştüremezsiniz.
Aconcagua

Aslında ya Almanca 'ß', derseniz ToUpper()birçok sistemde 'SS' e dönüşecek. Yani bu da aslında gidiş-dönüş mümkün değil.
Sebastian

Microsoft, kodu büyük harf karşılaştırmaları yapmak için optimize ettiyse, bunun nedeni büyük harfler için ASCII kodunun yalnızca iki basamaklı 65-90 iken ASCII kodunun 3 basamak içeren küçük harf 97-122'nin (daha fazla işlem yapılması gerekir)
Medo Medo

Hem "ϱ" hem de "ς" kelimelerinin kendilerinin geldiği unutulmamalıdır ToUpperInvariant(), bu nedenle büyük
harfin

Bu cevap, alakalı görünmüyor. Microsoft bağlantısına göre, bu yalnızca bir dizenin yerel ayarını değiştirirken önemlidir : "Bir gidiş-dönüş yapmak, karakterleri bir yerel ayardan, karakter verilerini farklı şekilde temsil eden başka bir yerel ayara dönüştürmek ve ardından orijinal karakterleri doğru bir şekilde almak anlamına gelir. dönüştürülmüş karakterler. " Ancak soru, farklı bir yerel ayara dönüştürmeyi içermez.
ToolmakerSteve

18

MSDN'ye göre dizeleri geçmek ve karşılaştırmaya durumu göz ardı etmesini söylemek daha etkilidir:

String.Compare (strA, strB, StringComparison.OrdinalIgnoreCase) çağrıya eşdeğerdir ( ancak daha hızlıdır )

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

Bu karşılaştırmalar hala çok hızlı.

Elbette, bir dizgiyi defalarca karşılaştırıyorsanız, bu geçerli olmayabilir.


11

Daha küçük harfli girişlere sahip olma eğiliminde olan dizelere dayanarak, ToLower teorik olarak daha hızlı olmalıdır (çok sayıda karşılaştırma, ancak birkaç atama).

Karşılaştırarak böylece - C olarak, ya, bir bayt karşılaştırma aslında (örneğin C dizeleri veya C ++ STL? Dize türü gibi) her dizenin bireysel olarak erişilebilir elemanlar kullanılırken UPPERfarksız lower.

Sinsi olsaydınız ve dizelerinizi longdizilere yüklediyseniz, tüm dizede çok hızlı bir karşılaştırma elde edersiniz çünkü bir seferde 4 baytı karşılaştırabilir. Ancak, yükleme süresi buna değmeyebilir.

Hangisinin daha hızlı olduğunu neden bilmeniz gerekiyor? Metrik bir karşılaştırmalar yapmadığınız sürece, birkaç döngü daha hızlı çalışan birinin genel yürütme hızı ile ilgisi yoktur ve kulağa erken optimizasyon gibi gelir :)


11
Hangisinin daha hızlı olduğunu neden bilmem gerektiği sorusuna cevap vermek gerekirse: Bilmeme gerek yok, sadece bilmek istiyorum. :) Bu, birisinin bir iddiada bulunduğunu görmek ("büyük harf dizilerini karşılaştırmak daha hızlıdır!" Gibi) ve bunun gerçekten doğru olup olmadığını ve / veya neden bu iddiada bulunduklarını bilmek istemesi durumudur.
Parappa

1
bu mantıklı - ben de böyle şeyleri sonsuza dek merak ediyorum :)
warren

1
C dizgileriyle, dizileri eşit olacak şekilde dönüştürmek sve tuzun dizilere dönüştürmek için, diziler eşitse, sonlandırıcı '\0'karakteri bulana kadar s ve t'den aşağı inmeniz gerekir (veya dizelerin sonundaki çöpleri karşılaştırabilirsiniz, tanımlanmamış davranışa neden olan geçersiz bir bellek erişimi olabilir). Peki öyleyse neden tek tek karakterler üzerinde yürürken karşılaştırmalar yapmıyoruz? C ++ dizeleri ile, muhtemelen uzunluğunu almak ve edebilirsiniz .c_str(), döküm bir etmek long *ve uzunlukta bir önek karşılaştırın .size() - .size()%(sizeof long). Bana biraz şüpheli görünüyor, tho.
Jonas Kölker

JonasKölker @ - bir diziye dize yüklerken longs sadece karşılaştırma aşkına aptalca olurdu. Ama eğer bunu "çok" yapıyorsanız - yapılması için olası bir argüman görebilirim .
warren

5

Microsoft optimize etti ToUpperInvariant(), değil ToUpper(). Aradaki fark, değişmezliğin daha kültür dostu olmasıdır. Kültürde farklılık gösterebilen dizeler üzerinde büyük / küçük harfe duyarlı olmayan karşılaştırmalar yapmanız gerekiyorsa, Değişmez kullanın, aksi takdirde değişmez dönüşümün performansı önemli olmamalıdır.

ToUpper () veya ToLower () 'ın daha hızlı olup olmadığını söyleyemem. Performansın bu kadar önemli olduğu bir durum yaşamadığımdan beri hiç denemedim.


Microsoft, kodu büyük harf karşılaştırmaları yapmak için optimize ettiyse, bunun nedeni büyük harfler için ASCII kodunun yalnızca iki basamaklı 65 - 90 iken ASCII kodunun 3 basamak içeren Küçük harf 97-122'den (daha fazla işlem yapılması gerekir) olmasından mı kaynaklanmaktadır?
Medo Medo

4
@Medo Optimizasyonun kesin nedenlerini hatırlamıyorum, ancak 2'ye 3 basamak neredeyse kesinlikle neden değildir, çünkü tüm harfler ikili sayılar olarak saklanır, bu nedenle ondalık basamaklar, depolanma şekillerine göre gerçekten bir anlam ifade etmez.
Dan Herbert

3

C # ile dize karşılaştırması yapıyorsanız, her iki dizeyi de büyük veya küçük harfe dönüştürmek yerine .Equals () öğesini kullanmak önemli ölçüde daha hızlıdır. .Equals () kullanmanın bir diğer büyük artısı, 2 yeni büyük / küçük harf dizgisine daha fazla bellek ayrılmamış olmasıdır.


4
Ve bonus olarak, doğru seçenekleri seçerseniz, aslında size doğru sonuçları verecektir :)
Jon Skeet

0

Gerçekten hiç önemli olmamalı. ASCII karakterleriyle, kesinlikle önemli değil - sadece birkaç karşılaştırma ve her iki yön için de biraz ters. Unicode biraz daha karmaşık olabilir, çünkü durumu garip şekillerde değiştiren bazı karakterler vardır, ancak metniniz bu özel karakterlerle dolu olmadığı sürece gerçekten herhangi bir fark olmamalıdır.


0

Doğru yapmak, küçük harfe çevirirseniz, küçük, önemsiz bir hız avantajı olmalıdır, ancak bu, çoğunun ima ettiği gibi, kültüre bağlıdır ve işlevde değil, dönüştürdüğünüz dizelerde miras alınır (birçok küçük harf belleğe birkaç atama anlamına gelir) - çok sayıda büyük harf içeren bir dizeniz varsa büyük harfe dönüştürme daha hızlıdır.


0

Bununla ilgili bazı gerçek veriler istedim, bu yüzden veya ile başarısız olan iki bayt karakterin tam listesini çıkardım . Daha sonra aşağıdaki testi yaptım:ToLowerToUpper

using System;

class Program {
   static void Main() {
      char[][] pairs = {
new[]{'\u00E5','\u212B'},new[]{'\u00C5','\u212B'},new[]{'\u0399','\u1FBE'},
new[]{'\u03B9','\u1FBE'},new[]{'\u03B2','\u03D0'},new[]{'\u03B5','\u03F5'},
new[]{'\u03B8','\u03D1'},new[]{'\u03B8','\u03F4'},new[]{'\u03D1','\u03F4'},
new[]{'\u03B9','\u1FBE'},new[]{'\u0345','\u03B9'},new[]{'\u0345','\u1FBE'},
new[]{'\u03BA','\u03F0'},new[]{'\u00B5','\u03BC'},new[]{'\u03C0','\u03D6'},
new[]{'\u03C1','\u03F1'},new[]{'\u03C2','\u03C3'},new[]{'\u03C6','\u03D5'},
new[]{'\u03C9','\u2126'},new[]{'\u0392','\u03D0'},new[]{'\u0395','\u03F5'},
new[]{'\u03D1','\u03F4'},new[]{'\u0398','\u03D1'},new[]{'\u0398','\u03F4'},
new[]{'\u0345','\u1FBE'},new[]{'\u0345','\u0399'},new[]{'\u0399','\u1FBE'},
new[]{'\u039A','\u03F0'},new[]{'\u00B5','\u039C'},new[]{'\u03A0','\u03D6'},
new[]{'\u03A1','\u03F1'},new[]{'\u03A3','\u03C2'},new[]{'\u03A6','\u03D5'},
new[]{'\u03A9','\u2126'},new[]{'\u0398','\u03F4'},new[]{'\u03B8','\u03F4'},
new[]{'\u03B8','\u03D1'},new[]{'\u0398','\u03D1'},new[]{'\u0432','\u1C80'},
new[]{'\u0434','\u1C81'},new[]{'\u043E','\u1C82'},new[]{'\u0441','\u1C83'},
new[]{'\u0442','\u1C84'},new[]{'\u0442','\u1C85'},new[]{'\u1C84','\u1C85'},
new[]{'\u044A','\u1C86'},new[]{'\u0412','\u1C80'},new[]{'\u0414','\u1C81'},
new[]{'\u041E','\u1C82'},new[]{'\u0421','\u1C83'},new[]{'\u1C84','\u1C85'},
new[]{'\u0422','\u1C84'},new[]{'\u0422','\u1C85'},new[]{'\u042A','\u1C86'},
new[]{'\u0463','\u1C87'},new[]{'\u0462','\u1C87'}
      };
      int upper = 0, lower = 0;
      foreach (char[] pair in pairs) {
         Console.Write(
            "U+{0:X4} U+{1:X4} pass: ",
            Convert.ToInt32(pair[0]),
            Convert.ToInt32(pair[1])
         );
         if (Char.ToUpper(pair[0]) == Char.ToUpper(pair[1])) {
            Console.Write("ToUpper ");
            upper++;
         } else {
            Console.Write("        ");
         }
         if (Char.ToLower(pair[0]) == Char.ToLower(pair[1])) {
            Console.Write("ToLower");
            lower++;
         }
         Console.WriteLine();
      }
      Console.WriteLine("upper pass: {0}, lower pass: {1}", upper, lower);
   }
}

Aşağıdaki sonuç. Not Ayrıca Invariantsürümlerle de test ettim ve sonuç tamamen aynıydı. İlginç bir şekilde, çiftlerden biri her ikisinde de başarısız oluyor. Ancak buna göre ToUpper en iyi seçenektir .

U+00E5 U+212B pass:         ToLower
U+00C5 U+212B pass:         ToLower
U+0399 U+1FBE pass: ToUpper
U+03B9 U+1FBE pass: ToUpper
U+03B2 U+03D0 pass: ToUpper
U+03B5 U+03F5 pass: ToUpper
U+03B8 U+03D1 pass: ToUpper
U+03B8 U+03F4 pass:         ToLower
U+03D1 U+03F4 pass:
U+03B9 U+1FBE pass: ToUpper
U+0345 U+03B9 pass: ToUpper
U+0345 U+1FBE pass: ToUpper
U+03BA U+03F0 pass: ToUpper
U+00B5 U+03BC pass: ToUpper
U+03C0 U+03D6 pass: ToUpper
U+03C1 U+03F1 pass: ToUpper
U+03C2 U+03C3 pass: ToUpper
U+03C6 U+03D5 pass: ToUpper
U+03C9 U+2126 pass:         ToLower
U+0392 U+03D0 pass: ToUpper
U+0395 U+03F5 pass: ToUpper
U+03D1 U+03F4 pass:
U+0398 U+03D1 pass: ToUpper
U+0398 U+03F4 pass:         ToLower
U+0345 U+1FBE pass: ToUpper
U+0345 U+0399 pass: ToUpper
U+0399 U+1FBE pass: ToUpper
U+039A U+03F0 pass: ToUpper
U+00B5 U+039C pass: ToUpper
U+03A0 U+03D6 pass: ToUpper
U+03A1 U+03F1 pass: ToUpper
U+03A3 U+03C2 pass: ToUpper
U+03A6 U+03D5 pass: ToUpper
U+03A9 U+2126 pass:         ToLower
U+0398 U+03F4 pass:         ToLower
U+03B8 U+03F4 pass:         ToLower
U+03B8 U+03D1 pass: ToUpper
U+0398 U+03D1 pass: ToUpper
U+0432 U+1C80 pass: ToUpper
U+0434 U+1C81 pass: ToUpper
U+043E U+1C82 pass: ToUpper
U+0441 U+1C83 pass: ToUpper
U+0442 U+1C84 pass: ToUpper
U+0442 U+1C85 pass: ToUpper
U+1C84 U+1C85 pass: ToUpper
U+044A U+1C86 pass: ToUpper
U+0412 U+1C80 pass: ToUpper
U+0414 U+1C81 pass: ToUpper
U+041E U+1C82 pass: ToUpper
U+0421 U+1C83 pass: ToUpper
U+1C84 U+1C85 pass: ToUpper
U+0422 U+1C84 pass: ToUpper
U+0422 U+1C85 pass: ToUpper
U+042A U+1C86 pass: ToUpper
U+0463 U+1C87 pass: ToUpper
U+0462 U+1C87 pass: ToUpper
upper pass: 46, lower pass: 8
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.