ตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก


86

เมื่อทำการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กหรือใหญ่การแปลงสตริงเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กจะมีประสิทธิภาพมากกว่ากัน? มันสำคัญหรือไม่?

มีคำแนะนำในโพสต์ SO นี้ว่า C # มีประสิทธิภาพมากกว่ากับ ToUpper เนื่องจาก "Microsoft ปรับให้เหมาะสมที่สุด" แต่ฉันได้อ่านอาร์กิวเมนต์นี้ด้วยว่าการแปลง ToLower กับ ToUpper นั้นขึ้นอยู่กับว่าสตริงของคุณมีอะไรมากกว่าและโดยทั่วไปสตริงจะมีอักขระตัวพิมพ์เล็กมากกว่าซึ่งทำให้ ToLower มีประสิทธิภาพมากขึ้น

โดยเฉพาะอย่างยิ่งฉันต้องการทราบ:

  • มีวิธีเพิ่มประสิทธิภาพ ToUpper หรือ ToLower ให้เร็วกว่าอีกแบบหรือไม่?
  • เร็วกว่าที่จะทำการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กหรือใหญ่ระหว่างสตริงตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กและเพราะเหตุใด
  • มีสภาพแวดล้อมการเขียนโปรแกรมหรือไม่ (เช่น C, C #, Python อะไรก็ได้) ที่กรณีหนึ่งดีกว่าอีกกรณีอย่างชัดเจนและเพราะเหตุใด

คำตอบ:


90

การแปลงเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กเพื่อทำการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กหรือใหญ่นั้นไม่ถูกต้องเนื่องจากคุณลักษณะ "น่าสนใจ" ของบางวัฒนธรรมโดยเฉพาะตุรกี ให้ใช้StringComparerกับตัวเลือกที่เหมาะสมแทน

MSDN มีแนวทางที่ดีในการจัดการสตริง คุณอาจต้องการตรวจสอบว่ารหัสของคุณผ่านการทดสอบตุรกีหรือไม่

ความคิดเห็นหมายเหตุนีลรอบ: แก้ไขลำดับการเปรียบเทียบกรณีตาย ดินแดนทั้งหมดนี้ค่อนข้างมืดมน :(


15
ใช่ StringComparer ดีมาก แต่คำถามไม่ได้รับคำตอบ ... ในสถานการณ์ที่คุณไม่สามารถใช้ StringComparer เช่นคำสั่ง swtich กับสตริง ฉันควร ToUpper หรือ ToLower ในสวิตช์?
joshperry

7
ใช้ StringComparer และ "if" / "else" แทนการใช้ ToUpper หรือ ToLower
Jon Skeet

5
จอห์นฉันรู้ว่าการแปลงเป็นตัวพิมพ์เล็กนั้นไม่ถูกต้อง แต่ฉันไม่เคยได้ยินมาว่าการแปลงเป็นตัวพิมพ์ใหญ่นั้นไม่ถูกต้อง คุณสามารถเสนอตัวอย่างหรือข้อมูลอ้างอิงได้หรือไม่? บทความ MSDN ที่คุณเชื่อมโยงกล่าวว่า: "การเปรียบเทียบที่ทำโดยใช้ OrdinalIgnoreCase เป็นพฤติกรรมของการเรียกสองสาย: การเรียก ToUpperInvariant กับอาร์กิวเมนต์สตริงทั้งสองและทำการเปรียบเทียบแบบลำดับ" ในส่วนที่ชื่อว่า "Ordinal String Operations" จะมีการระบุซ้ำในโค้ด
Neil

2
@ นีล: น่าสนใจฉันไม่เคยเห็นเลยสักนิด สำหรับการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ตามลำดับฉันคิดว่ามันยุติธรรมเพียงพอแล้ว มันต้องเลือกบางอย่างหลังจากทั้งหมด สำหรับการเปรียบเทียบที่คำนึงถึงกรณีและปัญหาทางวัฒนธรรมฉันคิดว่ายังมีที่ว่างสำหรับพฤติกรรมแปลก ๆ จะชี้ความคิดเห็นของคุณในคำตอบ ...
Jon Skeet

4
@Triynko: ฉันคิดว่าสิ่งสำคัญคือต้องมุ่งเน้นไปที่ความถูกต้องเป็นหลักโดยประเด็นที่ว่าการตอบผิดอย่างรวดเร็วมักจะไม่ดีไปกว่าการได้รับคำตอบที่ผิดอย่างช้าๆ
Jon Skeet

25

จากMicrosoftบน MSDN:

แนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้สตริงใน. NET Framework

คำแนะนำสำหรับการใช้งานสตริง

ทำไม? จากMicrosoft :

ทำให้สตริงปกติเป็นตัวพิมพ์ใหญ่

มีอักขระกลุ่มเล็ก ๆ ที่เมื่อแปลงเป็นตัวพิมพ์เล็กไม่สามารถเดินทางไปกลับได้

อะไรคือตัวอย่างของตัวละครที่ไม่สามารถเดินทางไปกลับได้?

  • เริ่มต้น : สัญลักษณ์กรีกโร (U + 03f1) ϱ
  • ตัวพิมพ์ใหญ่: Capital Greek Rho (U + 03a1) Ρ
  • ตัวพิมพ์เล็ก:กรีกตัวเล็ก Rho (U + 03c1) ρ

ϱ, Ρ , ρ

.NET ซอ

Original: ϱ
ToUpper: Ρ
ToLower: ρ

นั่นคือเหตุผลว่าทำไมหากคุณต้องการทำการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่คุณจะแปลงสตริงเป็นตัวพิมพ์ใหญ่ไม่ใช่ตัวพิมพ์เล็ก

ดังนั้นถ้าคุณต้องเลือกอย่างใดอย่างหนึ่งให้เลือกตัวพิมพ์ใหญ่


3
กลับไปที่คำตอบของคำถามเดิม: มีภาษาที่รู้จักตัวแปรตัวพิมพ์เล็กมากกว่าหนึ่งตัวสำหรับตัวแปรตัวพิมพ์ใหญ่หนึ่งตัว เว้นแต่คุณจะทราบกฎว่าจะใช้การแทนค่าเมื่อใด (อีกตัวอย่างหนึ่งในภาษากรีก: ตัวอักษรซิกม่าขนาดเล็กคุณใช้σที่คำขึ้นต้นหรือตรงกลางςที่ท้ายคำ (ดูen.wikipedia.org/wiki/Sigma ) คุณไม่สามารถแปลงกลับเป็นตัวแปรตัวพิมพ์เล็กได้อย่างปลอดภัย
Aconcagua

ที่จริงแล้ว "ß" ของเยอรมันถ้าคุณเรียกToUpper()มันจะกลายเป็น "SS" ในหลาย ๆ ระบบ ดังนั้นนี่จึงไม่สามารถไป - กลับได้เช่นกัน
Sebastian

หาก Microsoft ได้ปรับรหัสให้เหมาะสมสำหรับการเปรียบเทียบตัวพิมพ์ใหญ่เป็นเพราะรหัส ASCII สำหรับตัวอักษรตัวพิมพ์ใหญ่มีเพียงสองหลัก 65-90 ในขณะที่รหัส ASCII ตัวพิมพ์เล็ก 97-122 ซึ่งมี 3 หลัก (ต้องการการประมวลผลเพิ่มเติม)
Medo Medo

ควรสังเกตว่าทั้ง "ϱ" และ "ς" กลับจากตัวเองToUpperInvariant()ดังนั้นจึงเป็นเรื่องดีที่จะได้เห็นตัวอย่างจริงว่าทำไมตัวพิมพ์ใหญ่จึงดีกว่าตัวพิมพ์เล็ก
max630

คำตอบนี้ดูเหมือนจะไม่เกี่ยวข้อง ตามลิงค์ของ Microsoft สิ่งนี้มีความสำคัญเฉพาะเมื่อเปลี่ยนภาษาของสตริง: "ในการเดินทางไปกลับหมายถึงการแปลงอักขระจากสถานที่หนึ่งไปยังอีกสถานที่หนึ่งที่แสดงถึงข้อมูลอักขระที่แตกต่างกันจากนั้นเพื่อดึงอักขระดั้งเดิมจาก อักขระที่แปลงแล้ว " แต่คำถามไม่เกี่ยวข้องกับการแปลงเป็นภาษาอื่น
ToolmakerSteve

18

ตามMSDNมีประสิทธิภาพมากกว่าในการส่งผ่านสตริงและบอกการเปรียบเทียบเพื่อละเว้นกรณี:

String.Compare (strA, strB, StringComparison.OrdinalIgnoreCase) เทียบเท่ากับการเรียก ( แต่เร็วกว่า )

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

การเปรียบเทียบเหล่านี้ยังเร็วมาก

แน่นอนว่าหากคุณกำลังเปรียบเทียบสตริงหนึ่งซ้ำแล้วซ้ำอีกสิ่งนี้อาจไม่เกิดขึ้น


11

ตามสตริงที่มีแนวโน้มที่จะมีรายการตัวพิมพ์เล็กมากกว่าในทางทฤษฎี ToLower ควรจะเร็วกว่า (เปรียบเทียบได้มาก แต่มีการกำหนดน้อย)

ใน C หรือเมื่อใช้องค์ประกอบที่สามารถเข้าถึงได้ทีละรายการของแต่ละสตริง (เช่นสตริง C หรือประเภทสตริงของ STL ใน C ++) มันเป็นการเปรียบเทียบแบบไบต์ดังนั้นการเปรียบเทียบUPPERจึงไม่แตกต่างจากlower .

หากคุณส่อเสียดและโหลดสตริงของคุณลงในlongอาร์เรย์แทนคุณจะได้รับการเปรียบเทียบที่รวดเร็วมากกับสตริงทั้งหมดเนื่องจากสามารถเปรียบเทียบได้ครั้งละ 4 ไบต์ อย่างไรก็ตามเวลาในการโหลดอาจทำให้ไม่คุ้มค่า

ทำไมต้องรู้ว่าอันไหนเร็วกว่ากัน? เว้นแต่ว่าคุณกำลังทำการเปรียบเทียบเมตริกการทำงานสองสามรอบเร็วขึ้นนั้นไม่เกี่ยวข้องกับความเร็วของการดำเนินการโดยรวมและดูเหมือนการเพิ่มประสิทธิภาพก่อนกำหนด :)


11
เพื่อตอบคำถามว่าทำไมฉันต้องรู้ว่าอันไหนเร็วกว่า: ฉันไม่จำเป็นต้องรู้ฉันแค่อยากรู้ :) เป็นเพียงกรณีของการเห็นใครบางคนอ้างสิทธิ์ (เช่น "การเปรียบเทียบสตริงตัวพิมพ์ใหญ่เร็วกว่า!") และต้องการทราบว่าเป็นเรื่องจริงหรือไม่และ / หรือเหตุใดพวกเขาจึงอ้างสิทธิ์นั้น
Parappa

1
นั่นก็สมเหตุสมผล - ฉันก็อยากรู้เรื่องแบบนี้ตลอดไป :)
warren

1
ด้วยสตริง C ในการแปลงsและtเป็นอาร์เรย์ของ longs เพื่อให้สตริงมีค่าเท่ากันถ้าอาร์เรย์เท่ากันคุณต้องเดินลง s และ t จนกว่าคุณจะพบ'\0'อักขระยุติ(หรือมิฉะนั้นคุณอาจเปรียบเทียบขยะที่ผ่านจุดสิ้นสุดของสตริง ซึ่งอาจเป็นการเข้าถึงหน่วยความจำที่ผิดกฎหมายซึ่งก่อให้เกิดพฤติกรรมที่ไม่ได้กำหนด) แต่ทำไมไม่ทำการเปรียบเทียบในขณะที่เดินดูตัวละครทีละตัว? ด้วย C ++ สตริงคุณอาจจะได้รับความยาวและความ.c_str()หล่อไปและเปรียบเทียบคำนำหน้าของความยาวlong * .size() - .size()%(sizeof long)ดูคาวไปหน่อยสำหรับฉัน
Jonas Kölker

@ JonasKölker - การโหลดสตริงลงในอาร์เรย์ของlongs เพียงเพื่อประโยชน์ในการเปรียบเทียบจะเป็นเรื่องโง่เขลา แต่ถ้าคุณกำลังทำมัน "มาก" - ฉันจะได้เห็นความเป็นไปได้โต้แย้งให้มันทำได้
warren

5

ไมโครซอฟท์มีการเพิ่มประสิทธิภาพไม่ได้ToUpperInvariant() ToUpper()ความแตกต่างคือความไม่แปรเปลี่ยนเป็นมิตรกับวัฒนธรรมมากกว่า หากคุณจำเป็นต้องทำการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ในสตริงที่อาจแตกต่างกันไปตามวัฒนธรรมให้ใช้ Invariant มิฉะนั้นประสิทธิภาพของการแปลงค่าคงที่ไม่สำคัญ

ฉันไม่สามารถพูดได้ว่า ToUpper () หรือ ToLower () เร็วกว่า ฉันไม่เคยลองมาก่อนเพราะฉันไม่เคยมีสถานการณ์ที่ประสิทธิภาพสำคัญขนาดนั้น


หาก Microsoft ได้ปรับรหัสให้เหมาะสมสำหรับการเปรียบเทียบตัวพิมพ์ใหญ่เป็นเพราะรหัส ASCII สำหรับอักษรตัวพิมพ์ใหญ่มีเพียงสองหลัก 65-90 ในขณะที่รหัส ASCII ตัวพิมพ์เล็ก 97-122 ซึ่งมี 3 หลัก (ต้องการการประมวลผลเพิ่มเติม)
Medo Medo

4
@Medo ฉันจำเหตุผลที่แน่นอนของการเพิ่มประสิทธิภาพไม่ได้ แต่ตัวเลข 2 vs 3 แทบจะไม่ใช่เหตุผลเนื่องจากตัวอักษรทั้งหมดถูกจัดเก็บเป็นเลขฐานสองดังนั้นเลขฐานสิบจึงไม่มีความหมายตามวิธีการจัดเก็บ
Dan Herbert

3

หากคุณกำลังทำการเปรียบเทียบสตริงใน C # จะเร็วกว่าอย่างมากที่จะใช้. Equals () แทนที่จะแปลงสตริงทั้งสองเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็ก ข้อดีอีกอย่างสำหรับการใช้. Equals () คือหน่วยความจำที่เพิ่มขึ้นไม่ได้ถูกจัดสรรสำหรับสตริงตัวพิมพ์ใหญ่ / ตัวพิมพ์เล็กใหม่ 2 ตัว


4
และเป็นโบนัสหากคุณเลือกตัวเลือกที่ถูกต้องมันจะให้ผลลัพธ์ที่ถูกต้องจริงๆ :)
Jon Skeet

0

มันไม่ควรสำคัญเลย ด้วยอักขระ ASCII มันไม่สำคัญอย่างแน่นอนมันเป็นเพียงการเปรียบเทียบเล็กน้อยและพลิกไปทางใดทางหนึ่ง Unicode อาจจะซับซ้อนกว่าเล็กน้อยเนื่องจากมีอักขระบางตัวที่เปลี่ยนกรณีด้วยวิธีแปลก ๆ แต่ก็ไม่ควรมีความแตกต่างใด ๆ เว้นแต่ข้อความของคุณจะเต็มไปด้วยอักขระพิเศษเหล่านั้น


0

การทำอย่างถูกต้องควรมีข้อได้เปรียบด้านความเร็วเล็กน้อยที่ไม่มีนัยสำคัญหากคุณแปลงเป็นตัวพิมพ์เล็ก แต่สิ่งนี้มีคำใบ้ขึ้นอยู่กับวัฒนธรรมและไม่ได้สืบทอดในฟังก์ชัน แต่ในสตริงที่คุณแปลง (ตัวอักษรตัวพิมพ์เล็กจำนวนมาก หมายถึงการกำหนดหน่วยความจำเพียงเล็กน้อย) - การแปลงเป็นตัวพิมพ์ใหญ่จะเร็วกว่าหากคุณมีสตริงที่มีตัวอักษรตัวพิมพ์ใหญ่จำนวนมาก


0

ฉันต้องการข้อมูลจริงบางอย่างเกี่ยวกับเรื่องนี้ดังนั้นผมจึงดึงรายการเต็มรูปแบบของตัวละครทั้งสองไบต์ที่ล้มเหลวด้วยหรือToLower ToUpperจากนั้นฉันทำการทดสอบนี้ด้านล่าง:

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);
   }
}

ผลลัพธ์ด้านล่าง โปรดทราบว่าฉันทดสอบกับInvariantเวอร์ชันแล้วด้วยและผลลัพธ์ก็เหมือนกันทุกประการ ที่น่าสนใจคือคู่ใดคู่หนึ่งล้มเหลวกับทั้งคู่ แต่อยู่บนพื้นฐานนี้ ToUpper เป็นตัวเลือกที่ดีที่สุด

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.