ความคิดที่อยู่เบื้องหลัง ^ = 32 คืออะไรที่แปลงตัวอักษรตัวพิมพ์เล็กเป็นตัวพิมพ์ใหญ่และในทางกลับกัน?


146

ฉันกำลังแก้ไขปัญหาบางอย่างใน codeforces โดยปกติฉันตรวจสอบก่อนว่าอักขระเป็นตัวอักษรภาษาอังกฤษตัวบนหรือล่างจากนั้นลบหรือเพิ่ม32เพื่อแปลงเป็นตัวอักษรที่เกี่ยวข้อง แต่ฉันพบว่ามีคนทำ^= 32เพื่อทำสิ่งเดียวกัน นี่มันคือ:

char foo = 'a';
foo ^= 32;
char bar = 'A';
bar ^= 32;
cout << foo << ' ' << bar << '\n'; // foo is A, and bar is a

ฉันค้นหาคำอธิบายสำหรับเรื่องนี้แล้วและไม่พบคำตอบ เหตุใดจึงใช้งานได้


5
en.wikipedia.org/wiki/File:USASCII_code_chart.pngเคล็ดลับ: คุณสามารถแปลง@ลงใน ^ 32`โดยใช้
KamilCuk

112
FWIW มันไม่ได้ "ทำงาน" จริงๆ มันใช้งานได้กับชุดตัวละครนี้โดยเฉพาะ แต่มีอีกหลายชุดที่ไม่ควรใช้toupperและtolowerเพื่อเปลี่ยนเคส
NathanOliver

7
บางครั้งกับการแข่งขันออนไลน์ "ความคิด" คือการเขียนโค้ดในลักษณะที่สับสนว่ามันจะไม่ผ่านการตรวจสอบอย่างจริงจัง;)
idclev 463035818

21
^ = กำลังเปลี่ยนค่าโดยใช้ XOR ตัวอักษรตัวพิมพ์ใหญ่ ASCII มีศูนย์ในบิตที่สอดคล้องกันในขณะที่ตัวอักษรตัวเล็กมีหนึ่ง ที่กล่าวว่าโปรดอย่า! ใช้รูทีนอักขระ (ยูนิโค้ด) ที่เหมาะสมเพื่อแปลงระหว่างตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ ยุคของแค่ ASCII หายไปนานแล้ว
Hans-Martin Mosner

14
ไม่ใช่ว่ามันใช้ได้กับตัวละครบางตัวเท่านั้น แม้ว่าเราจะถือว่าโลกทั้งใบเป็น UTF-8 (ซึ่งอย่างน้อยก็อาจเป็นเป้าหมายของอุดมคติยูโทเปียที่ดี) แต่ก็ใช้ได้กับตัวอักษร 26 ตัวAถึงZเท่านั้น ไม่เป็นไรตราบใดที่คุณสนใจภาษาอังกฤษเท่านั้น (และอย่าใช้การสะกดคำ "naïve" คำเช่น "café" หรือชื่อที่มีการกำกับ ... ) แต่โลกไม่ใช่แค่ภาษาอังกฤษ
ilkkachu

คำตอบ:


149

ลองดูที่ตารางรหัส ASCII ในรูปแบบไบนารี

A 1000001    a 1100001
B 1000010    b 1100010
C 1000011    c 1100011
...
Z 1011010    z 1111010

และ 32 คือ0100000ข้อแตกต่างระหว่างตัวพิมพ์เล็กและตัวพิมพ์ใหญ่เท่านั้น ดังนั้นการสลับบิตเป็นการสลับกรณีของตัวอักษร


49
"สลับเคส" * สำหรับ ASCII
Mooing Duck

39
@Mooing สำหรับ A-Za-z ใน ASCII เท่านั้น ตัวพิมพ์เล็กของ "[" ไม่ใช่ "{"
dbkk

21
@dbkk {สั้นกว่า[ดังนั้นจึงเป็นตัวพิมพ์เล็ก " ไม่มี? ตกลงฉันจะแสดงตัวเอง: D
Peter Badida

25
เรื่องเบ็ดเตล็ดอาหารอันโอชะ: ในพื้นที่ 7 บิตคอมพิวเตอร์เยอรมันได้ [] {|} แมปไปยังÄÖÜäöüเนื่องจากเราต้องการ Umlauts มากกว่าตัวละครเหล่านั้นดังนั้นในบริบทนั้น {(ä) จริงๆแล้วเป็นตัวพิมพ์เล็ก [(Ä)
Guntram Blohm สนับสนุนโมนิกา

14
@GuntramBlohm เรื่องสั้นเรื่องไม่สำคัญนี่คือเหตุผลที่เซิร์ฟเวอร์ IRC พิจารณา foobar[]และfoobar{}เป็นชื่อเล่นที่เหมือนกันเนื่องจากชื่อเล่นนั้นไม่ตรงตามตัวพิมพ์ใหญ่และ IRC มีต้นกำเนิดในสแกนดิเนเวีย :)
ZeroKnight

117

สิ่งนี้ใช้ความจริงมากกว่าค่า ASCII ที่ได้รับเลือกโดยคนฉลาดจริง ๆ

foo ^= 32;

นี้พลิก 6 บิตต่ำสุด1ของfoo(ธงพิมพ์ใหญ่ของการจัดเรียงของ ASCII) เปลี่ยน ASCII กรณีบนเพื่อกรณีที่ต่ำกว่าและในทางกลับกัน

+---+------------+------------+
|   | Upper case | Lower case |  32 is 00100000
+---+------------+------------+
| A | 01000001   | 01100001   |
| B | 01000010   | 01100010   |
|            ...              |
| Z | 01011010   | 01111010   |
+---+------------+------------+

ตัวอย่าง

'A' ^ 32

    01000001 'A'
XOR 00100000 32
------------
    01100001 'a'

'a' ^ 32 == 'A'และทรัพย์สินของแฮคเกอร์,

แจ้งให้ทราบ

C ++ ไม่จำเป็นต้องใช้ ASCII เพื่อเป็นตัวแทนของตัวละคร ที่แตกต่างก็คือEBCDIC เคล็ดลับนี้ใช้ได้กับแพลตฟอร์ม ASCII เท่านั้น วิธีแก้ปัญหาแบบพกพาที่มากขึ้นคือการใช้std::tolowerและstd::toupperด้วยโบนัสที่เสนอให้คำนึงถึงตำแหน่งที่ตั้ง

bool case_incensitive_equal(char lhs, char rhs)
{
    return std::tolower(lhs, std::locale{}) == std::tolower(rhs, std::locale{}); // std::locale{} optional, enable locale-awarness
}

assert(case_incensitive_equal('A', 'a'));

1)เมื่อ 32 คือ1 << 5(2 ถึงกำลัง 5) มันจะหมุนบิตที่ 6 (นับจาก 1)


16
EBCDIC ได้รับเลือกจากคนที่ฉลาดมากเช่นกัน: ใช้งานได้ดีกับการ์ดที่ถูกชก ASCII ซึ่งเป็นระเบียบ แต่นี่เป็นคำตอบที่ดี +1
Bathsheba

65
ฉันไม่รู้เกี่ยวกับ punch card แต่ ASCII ถูกใช้บนเทปกระดาษ นั่นเป็นสาเหตุที่อักขระลบถูกเข้ารหัสเป็น 1111111: ดังนั้นคุณสามารถทำเครื่องหมายอักขระใด ๆ ว่า "ลบ" โดยเจาะรูทั้งหมดในคอลัมน์ของมันลงบนเทป
dan04

23
@Bathsheba ในฐานะคนที่ไม่ได้ใช้ punchcard มันยากมากที่จะคาดเดาความคิดของฉันว่า EBCDIC ได้รับการออกแบบอย่างชาญฉลาด
ลอร์ด Farquaad

9
@ LordFarquaad IMHO ภาพวิกิพีเดียของวิธีการเขียนตัวอักษรบน punchcard เป็นภาพประกอบที่ชัดเจนเกี่ยวกับวิธีการ EBCDIC ทำให้รู้สึกบางส่วน (แต่ไม่รวมเห็น / vs S) สำหรับการเข้ารหัสนี้ en.wikipedia.org/wiki/EBCDIC#/media/…
Peteris

11
@ dan04 หมายเหตุที่ต้องพูดถึง "รูปแบบตัวพิมพ์เล็กของ 'MASSE' คืออะไร? สำหรับผู้ที่ไม่ทราบว่ามีสองคำในเยอรมันที่มีรูปแบบตัวพิมพ์ใหญ่เป็น MASSE; หนึ่งคือ "Masse" และอีกอันคือ "Maße" tolowerภาษาเยอรมันที่เหมาะสมไม่เพียงต้องการพจนานุกรม แต่ต้องสามารถแยกความหมายได้
Martin Bonner สนับสนุนโมนิก้า

35

ให้ฉันบอกว่านี่คือ - แม้ว่ามันจะดูฉลาด - แฮ็คที่โง่จริงๆ หากมีคนแนะนำสิ่งนี้ให้คุณในปี 2562 ให้ตีเขา ตีเขาอย่างหนักเท่าที่คุณสามารถ
แน่นอนคุณสามารถทำได้ในซอฟต์แวร์ของคุณเองซึ่งคุณและไม่มีใครใช้ถ้าคุณรู้ว่าคุณจะไม่ใช้ภาษาใด ๆ นอกจากภาษาอังกฤษ ไม่งั้นก็ไม่ไป

การแฮ็กนั้นพิสูจน์ได้ว่า "ตกลง" เมื่อ 30-35 ปีก่อนเมื่อคอมพิวเตอร์ไม่ได้ทำอะไรมากนักนอกจากภาษาอังกฤษใน ASCII และอาจเป็นหนึ่งในสองภาษาหลักของยุโรป แต่ ... ไม่มีอีกแล้ว

แฮ็คทำงานได้เนื่องจาก US-Latin บนและล่างแบ่ง0x20ออกจากกันและปรากฏในลำดับเดียวกันซึ่งแตกต่างกันเพียงเล็กน้อย ซึ่งอันที่จริงแล้วการแฮ็กบิตนี้เป็นการสลับ

ตอนนี้ผู้คนกำลังสร้างหน้ารหัสสำหรับยุโรปตะวันตกและต่อมากลุ่ม Unicode นั้นฉลาดพอที่จะรักษารูปแบบนี้เช่นภาษาเยอรมัน Umlauts และสระที่เน้นเสียงภาษาฝรั่งเศส ไม่เช่นนั้นßซึ่ง (จนกว่าจะมีคนเชื่อว่ากลุ่ม Unicode ในปี 2017 และนิตยสารสิ่งพิมพ์ข่าวปลอมฉบับใหญ่เขียนเกี่ยวกับเรื่องนี้จริง ๆ แล้วเชื่อว่า Duden - ไม่มีความเห็นเกี่ยวกับเรื่องนั้น) ไม่ได้มีอยู่เหมือนกัน (เปลี่ยนเป็น SS) . ตอนนี้ก็ไม่อยู่ในฐานะที่ Versal แต่ทั้งสองมีตำแหน่งออกจากกันไม่ได้0x1DBF0x20

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

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

อย่าแม้แต่จะคิดว่าแฮ็คนี้จะทำอะไรกับสิ่งที่เหมือนไทยหรือจีน (มันจะให้เรื่องไร้สาระที่สมบูรณ์)

การประหยัดซีพียูสองร้อยรอบอาจคุ้มค่ามากเมื่อ 30 ปีที่แล้ว แต่ทุกวันนี้ไม่มีข้อแก้ตัวใด ๆ สำหรับการแปลงสตริงอย่างถูกต้อง มีฟังก์ชั่นห้องสมุดสำหรับการทำงานที่ไม่สำคัญนี้
เวลาที่ใช้ในการแปลงข้อความหลายสิบกิโลไบต์อย่างถูกต้องเป็นสิ่งที่ไม่สำคัญในปัจจุบัน


2
ฉันเห็นด้วยอย่างยิ่ง - แม้ว่าจะเป็นความคิดที่ดีสำหรับโปรแกรมเมอร์ทุกคนที่จะรู้ว่าทำไมมันถึงได้ผล - อาจทำคำถามสัมภาษณ์ที่ดี .. สิ่งนี้ทำอะไรและควรใช้เมื่อไร :)
Bill K

33

มันทำงานได้เพราะตามที่เกิดขึ้นความแตกต่างระหว่าง 'a' และ A 'ใน ASCII และการเข้ารหัสที่ได้รับคือ 32 และ 32 ก็เป็นค่าของบิตที่หกเช่นกัน พลิกบิตที่ 6 ด้วยเอกสิทธิ์หรือดังนั้นแปลงระหว่างบนและล่าง


22

เป็นไปได้มากว่าการติดตั้งชุดอักขระจะเป็น ASCII ถ้าเราดูที่โต๊ะ:

ป้อนคำอธิบายรูปภาพที่นี่

เราเห็นว่ามีความแตกต่าง32ระหว่างค่าของตัวพิมพ์เล็กและตัวพิมพ์ใหญ่อย่างแน่นอน ดังนั้นถ้าเราทำ^= 32(ซึ่งเท่ากับการสลับบิตที่มีนัยสำคัญน้อยที่สุดเป็นอันดับที่ 6) มันจะเปลี่ยนระหว่างตัวพิมพ์เล็กและตัวพิมพ์ใหญ่

โปรดทราบว่ามันใช้งานได้กับสัญลักษณ์ทั้งหมดไม่ใช่แค่ตัวอักษร มันสลับตัวละครที่มีตัวละครที่เกี่ยวข้องที่บิตที่หกแตกต่างกันส่งผลให้คู่ของตัวละครที่มีการสลับไปมาระหว่าง สำหรับตัวอักษรตัวอักษรบน / ตัวอักษรที่เกี่ยวข้องนั้นจะจับคู่กัน NULการเปลี่ยนแปลงจะเข้าไปSpaceและวิธีอื่น ๆ และ@สลับกับ backtick โดยทั่วไปอักขระใด ๆ ในคอลัมน์แรกของแผนภูมินี้จะสลับกับอักขระหนึ่งคอลัมน์เหนือและจะใช้กับคอลัมน์ที่สามและสี่

ฉันจะไม่ใช้แฮ็คนี้เพราะไม่มีรับประกันว่ามันจะทำงานบนระบบใด ๆ เพียงแค่ใช้ToUpperและToLowerแทนและ queries เช่นisupper


2
มันใช้ไม่ได้กับตัวอักษรทั้งหมดที่มีความแตกต่างเป็น 32 มิฉะนั้นจะใช้งานได้ระหว่าง '@' และ ''!
Matthieu Brucher

2
@MatthieuBrucher มันใช้งานได้32 ^ 32คือ 0 ไม่ใช่ 64
NathanOliver

5
'@' และ '' ไม่ใช่ "ตัวอักษร" เฉพาะ[a-z]และ[A-Z]เป็น "ตัวอักษร" ส่วนที่เหลือเป็นเรื่องบังเอิญที่ปฏิบัติตามกฎเดียวกัน หากมีคนขอให้คุณ "ตัวพิมพ์ใหญ่]" มันจะเป็นอะไร? มันจะยังคงเป็น "]" - "}" ไม่ใช่ "ตัวพิมพ์ใหญ่" ของ "]"
freedomn-m

4
@MatthieuBrucher: อีกวิธีในการสร้างจุดนั้นคือช่วงตัวอักษรตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ไม่ข้าม%32ขอบเขต "การจัดตำแหน่ง" ในระบบการเข้ารหัส ASCII นี่คือเหตุผลที่ bit 0x20เป็นความแตกต่างเพียงอย่างเดียวระหว่างตัวอักษรตัวพิมพ์ใหญ่ / ตัวพิมพ์เล็ก หากไม่ใช่ในกรณีนี้คุณจะต้องเพิ่มหรือลบ0x20ไม่ใช่แค่สลับและสำหรับตัวอักษรบางตัวจะมีการดำเนินการเพื่อพลิกบิตที่สูงขึ้นอื่น ๆ (และการดำเนินการเดียวกันไม่สามารถสลับและการตรวจสอบตัวอักษรในสถานที่แรกจะยากเพราะคุณไม่|= 0x20สามารถบังคับ lcase)
Peter Cordes

2
+1 สำหรับเตือนให้ฉันนึกถึงการเข้าเยี่ยมชม asciitable.com ทั้งหมดเพื่อจ้องที่กราฟิกที่แน่นอนนั้น (และเวอร์ชัน ASCII เพิ่มเติม !!) สำหรับครั้งล่าสุดฉันไม่รู้ 15 หรือ 20 ปี?
AC

15

คำตอบที่ดีมากมายที่นี่ซึ่งอธิบายวิธีการทำงานของมัน แต่ทำไมมันถึงได้ผลเช่นนี้คือการปรับปรุงประสิทธิภาพ การทำงานของ Bitwise นั้นเร็วกว่าการทำงานอื่น ๆ ภายในโปรเซสเซอร์ คุณสามารถทำการเปรียบเทียบแบบตัวเล็กและตัวเล็กได้อย่างรวดเร็วโดยไม่ต้องดูบิตที่กำหนดตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ / ต่ำโดยเพียงแค่พลิกบิต (พวกที่ออกแบบตาราง ASCII นั้นดูค่อนข้างฉลาด)

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

https://en.wikipedia.org/wiki/Bitwise_operation

โดยทั่วไปแล้วโปรเซสเซอร์ราคาประหยัดแบบธรรมดาการดำเนินการระดับบิตจะเร็วกว่าการหารอย่างมากเร็วกว่าการคูณหลายเท่าและบางครั้งก็เร็วกว่าการเติม

หมายเหตุ: ฉันขอแนะนำให้ใช้ไลบรารีมาตรฐานสำหรับการทำงานกับสตริงด้วยเหตุผลหลายประการ (ความสามารถในการอ่านความถูกต้องความสะดวกในการพกพา ฯลฯ ) ใช้การพลิกบิตเฉพาะเมื่อคุณวัดประสิทธิภาพแล้วและนี่คือปัญหาคอขวดของคุณ


14

มันเป็นวิธีที่ ASCII ใช้งานได้ทั้งหมด

แต่ในการใช้ประโยชน์จากสิ่งนี้คุณกำลังยอมแพ้การพกพาเนื่องจาก C ++ ไม่ได้ยืนยันว่า ASCII เป็นการเข้ารหัส

นี่คือเหตุผลที่ฟังก์ชั่นstd::toupperและstd::tolowerมีการใช้งานในห้องสมุดมาตรฐาน C ++ - คุณควรใช้ฟังก์ชั่นเหล่านั้นแทน


6
มีโปรโตคอลที่ต้องการให้ใช้ ASCII เช่น DNS ในความเป็นจริงนั้นเซิร์ฟเวอร์ DNS บางตัวใช้ "0x20 หลอกลวง" เพื่อแทรกเอนโทรปีเพิ่มเติมลงในแบบสอบถาม DNS เพื่อเป็นกลไกในการต่อต้านการปลอมแปลง DNS ไม่คำนึงถึงขนาดตัวพิมพ์ แต่ก็ควรจะรักษาด้วยตัวพิมพ์เล็กและตัวเล็กดังนั้นหากส่งเคียวรีด้วยตัวพิมพ์เล็กและได้รับเคสเดิมกลับมาเป็นการบ่งชี้ที่ดีว่าการตอบสนองไม่ได้ถูกปลอมแปลงโดยบุคคลที่สาม
Alnitak

เป็นมูลค่าการกล่าวขวัญว่าการเข้ารหัสจำนวนมากยังคงมีการแสดงที่เหมือนกันสำหรับอักขระ ASCII มาตรฐาน (ไม่ขยาย) แต่ถ้าคุณกังวลเกี่ยวกับการเข้ารหัสที่แตกต่างกันคุณควรใช้ฟังก์ชั่นที่เหมาะสม
Captain Man

5
@CaptainMan: แน่นอน UTF-8 เป็นสิ่งที่สวยงามอย่างแท้จริง หวังว่ามันจะ "ซึมซับ" เข้าไปในมาตรฐาน C ++ ที่ IEEE754 มีสำหรับจุดลอยตัว
Bathsheba

11

ดูตารางที่สองที่http://www.catb.org/esr/faqs/things-every-hacker-once-knew/#_asciiและบันทึกย่อดังต่อไปนี้ทำซ้ำด้านล่าง:

โดยทั่วไปตัวปรับเปลี่ยนการควบคุมบนคีย์บอร์ดของคุณจะทำการล้างอักขระสามตัวแรกที่คุณพิมพ์ออกไปโดยปล่อยให้อยู่ด้านล่างห้าและทำแผนที่ให้อยู่ในช่วง 0..31 ตัวอย่างเช่น Ctrl-SPACE, Ctrl- @ และ Ctrl-`ทั้งหมดหมายถึงสิ่งเดียวกัน: NUL

คีย์บอร์ดเก่า ๆ ที่ใช้ในการทำ Shift เพียงแค่สลับ 32 หรือ 16 บิตขึ้นอยู่กับคีย์; นี่คือสาเหตุที่ความสัมพันธ์ระหว่างอักษรตัวเล็กและใหญ่ใน ASCII เป็นปกติและความสัมพันธ์ระหว่างตัวเลขและสัญลักษณ์และสัญลักษณ์บางคู่นั้นเป็นเรื่องปกติถ้าคุณเหล่มัน ASR-33 ซึ่งเป็นเทอร์มินัลตัวพิมพ์ใหญ่ทั้งหมดให้คุณสร้างอักขระเครื่องหมายวรรคตอนได้โดยไม่มีปุ่มสำหรับเลื่อน 16 บิต; ตัวอย่างเช่น Shift-K (0x4B) กลายเป็น [(0x5B)

ASCII ได้รับการออกแบบเพื่อให้สามารถใช้งานshiftและctrlแป้นคีย์บอร์ดได้โดยไม่ต้องใช้ctrlลอจิก(หรืออาจใช้เพื่อ) ตรรกะ - shiftอาจต้องใช้เพียงไม่กี่ประตู อย่างน้อยก็น่าจะเหมาะสมที่จะเก็บ wire wire เหมือนกับการเข้ารหัสอักขระอื่น ๆ (ไม่จำเป็นต้องมีการแปลงซอฟต์แวร์)

บทความที่เชื่อมโยงนี้ยังอธิบายถึงการประชุมแฮ็กเกอร์แปลก ๆ เช่นAnd control H does a single character and is an old^H^H^H^H^H classic joke.(ดูที่นี่ )


1
สามารถใช้ shift shift แทน ASCII w / ได้foo ^= (foo & 0x60) == 0x20 ? 0x10 : 0x20มากกว่าแม้ว่าจะเป็น ASCII เท่านั้นและไม่ฉลาดด้วยเหตุผลที่ระบุไว้ในคำตอบอื่น ๆ มันอาจจะสามารถปรับปรุงด้วยการเขียนโปรแกรมด้วยสาขาฟรี
Iiridayn

1
อาfoo ^= 0x20 >> !(foo & 0x40)จะง่ายกว่านี้ ยังเป็นตัวอย่างที่ดีว่าทำไมรหัส terse จึงถูกพิจารณาว่าอ่านไม่ได้ ^ _ ^
Iiridayn

8

Xoring with 32 (00100000 in binary) ตั้งค่าหรือรีเซ็ต bit ที่หก (จากขวา) นี่เทียบเท่ากับการเพิ่มหรือลบ 32 อย่างเคร่งครัด


2
อีกวิธีในการพูดแบบนี้ก็คือ XOR นั้นเป็นการเพิ่มโดยไม่ต้องพกพา
Peter Cordes

7

ตัวอักษรตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ช่วงไม่ข้าม%32ขอบเขต "การจัดตำแหน่ง" ในระบบการเข้ารหัส ASCII

นี่คือเหตุผลที่ bit 0x20เป็นความแตกต่างเพียงอย่างเดียวระหว่างตัวอักษรตัวใหญ่ / ตัวเล็กในรุ่นเดียวกัน

หากไม่ใช่ในกรณีนี้คุณจะต้องเพิ่มหรือลบ0x20ไม่ใช่แค่สลับและสำหรับตัวอักษรบางตัวจะมีการดำเนินการเพื่อพลิกบิตที่สูงขึ้นอื่น ๆ (และจะไม่มีการดำเนินการเดียวที่สามารถสลับได้และการตรวจสอบตัวอักษรในสถานที่แรกจะยากกว่าเพราะคุณไม่สามารถ | = 0x20 เพื่อบังคับให้ lcase)


ที่เกี่ยวข้องกับเทคนิค ASCII เท่านั้น: คุณสามารถตรวจสอบสำหรับอักขระ ASCII ตัวอักษรด้วยการบังคับให้เป็นตัวพิมพ์เล็กด้วยc |= 0x20แล้วตรวจสอบว่า c - 'a' <= ('z'-'a')(ไม่ได้ลงนาม) ดังนั้นแค่ 3 การทำงาน: + + SUB + CMP เทียบกับค่าคงที่ 25 แน่นอนว่าคอมไพเลอร์รู้วิธีเพิ่มประสิทธิภาพให้กับ(c>='a' && c<='z') asm เช่นนี้สำหรับคุณดังนั้นส่วนใหญ่คุณควรทำc|=0x20ส่วนของตัวเอง intมันค่อนข้างไม่สะดวกที่จะทำทุกสิ่งที่จำเป็นหล่อตัวเองโดยเฉพาะอย่างยิ่งในการทำงานรอบโปรโมชั่นเริ่มต้นจำนวนเต็มลงนาม

unsigned char lcase = y|0x20;
if (lcase - 'a' <= (unsigned)('z'-'a')) {   // lcase-'a' will wrap for characters below 'a'
    // c is alphabetic ASCII
}
// else it's not

ดูเพิ่มเติมที่การแปลงสตริงใน C ++ เป็นตัวพิมพ์ใหญ่ (สตริง SIMD toupperสำหรับ ASCII เท่านั้น, ปิดบังตัวถูกดำเนินการสำหรับ XOR โดยใช้การตรวจสอบนั้น)

และวิธีเข้าถึงอาร์เรย์ char และเปลี่ยนตัวอักษรตัวพิมพ์เล็กเป็นตัวพิมพ์ใหญ่และในทางกลับกัน (C ที่มี SIMD ภายในและ scalar x86 asm case-flip สำหรับอักขระ ASCII ตัวอักษรทำให้ผู้อื่นไม่ได้รับการแก้ไข)


เทคนิคเหล่านี้ส่วนใหญ่จะมีประโยชน์ก็ต่อเมื่อการเพิ่มประสิทธิภาพการประมวลผลข้อความด้วยมือ (เช่น SSE2 หรือ NEON) ด้วยมือเมื่อตรวจสอบว่าไม่มีchars ในเวกเตอร์ที่มีชุดบิตสูง (และดังนั้นจึงไม่มีไบต์เป็นส่วนหนึ่งของการเข้ารหัส UTF-8 แบบหลายไบต์สำหรับอักขระเดียวซึ่งอาจมีตัวผกผันด้านบน / ล่างแตกต่างกัน) หากคุณพบใด ๆ คุณสามารถถอยกลับไปที่เซนต์คิตส์และเนวิสสำหรับ 16 อันนี้หรือส่วนที่เหลือของสตริง

มีบางภาษาที่toupper()หรือtolower()ที่ตัวละครบางตัวในช่วง ASCII สร้างตัวละครที่อยู่นอกช่วงนั้นโดยเฉพาะอย่างยิ่งภาษาตุรกีที่ซึ่งฉัน and ıและİ↔ i ในพื้นที่เหล่านั้นคุณต้องมีการตรวจสอบที่ซับซ้อนกว่านี้หรืออาจไม่พยายามใช้การเพิ่มประสิทธิภาพนี้เลย


แต่ในบางกรณีคุณได้รับอนุญาตให้สมมติ ASCII แทน UTF-8 เช่นยูทิลิตี Unix ที่มีLANG=C(โลแคล POSIX) ไม่ใช่en_CA.UTF-8หรืออะไรก็ตาม

แต่ถ้าคุณสามารถตรวจสอบว่ามันปลอดภัยที่คุณสามารถtoupperสตริงยาวปานกลางเร็วกว่าการเรียกร้องtoupper()ในวง (ชอบ 5x) และครั้งสุดท้ายที่ฉันทดสอบกับ Boost 1.58มากมากเร็วกว่าboost::to_upper_copy<char*, std::string>()ซึ่งไม่โง่dynamic_castสำหรับตัวละครทุกตัว

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