ทำไมรหัสนี้เขียนย้อนหลังพิมพ์“ Hello World!”


261

นี่คือรหัสที่ฉันพบบนอินเทอร์เน็ต:

class M‮{public static void main(String[]a‭){System.out.print(new char[]
{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}    

รหัสนี้พิมพ์Hello World!ลงบนหน้าจอ คุณสามารถเห็นมันทำงานที่นี่ ฉันสามารถเห็นการpublic static void mainเขียนได้ชัดเจนแต่กลับด้านหลัง รหัสนี้ทำงานอย่างไร สิ่งนี้จะรวบรวมได้อย่างไร

แก้ไข:ฉันลองใช้รหัสนี้ใน IntellIJ และทำงานได้ดี อย่างไรก็ตามด้วยเหตุผลบางอย่างมันไม่ทำงานใน notepad ++ พร้อมกับ cmd ฉันยังไม่พบวิธีแก้ไขปัญหาดังกล่าวดังนั้นหากมีใครแสดงความคิดเห็นลงด้านล่าง


38
อันนี้มันตลก ... จะทำอะไรกับการสนับสนุน RTL?
ยูจีน Sh.

12
มีตัวอักษร Unicode # 8237 อยู่ ทันทีหลังจากMและ[]a: fileformat.info/info/unicode/char/202d/index.htmมันถูกเรียกว่า LEFT-TO-RIGHT OVERRIDE
Riiverside

45
xkcd บังคับ: xkcd.com/1137
Pac0

4
คุณสามารถเห็นสิ่งที่เกิดขึ้นที่นี่ได้อย่างง่ายดายโดยการเลือกข้อมูลโค้ดโดยใช้เมาส์
Andreas Rejbrand

14
niam diov citats cilbupเสียงเหมือนภาษิตละติน ..
มิก Mnemonic

คำตอบ:


250

มีอักขระที่มองไม่เห็นที่นี่ซึ่งเปลี่ยนวิธีการแสดงรหัส ใน Intellij สามารถพบได้โดยการคัดลอกโค้ดลงในสตริงว่าง ( "") ซึ่งแทนที่ด้วย Unicode escapes เอาเอฟเฟกต์ออกและเปิดเผยลำดับที่คอมไพเลอร์เห็น

นี่คือผลลัพธ์ของการคัดลอกวาง:

"class M\u202E{public static void main(String[]a\u202D){System.out.print(new char[]\n"+
        "{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}   "

อักขระซอร์สโค้ดจะถูกเก็บไว้ในคำสั่งนี้และคอมไพเลอร์ถือว่าพวกมันเป็นไปตามลำดับนี้ แต่มันก็แสดงต่างกัน

โปรดสังเกตว่า\u202Eอักขระซึ่งเป็นการแทนที่จากขวาไปซ้ายเริ่มบล็อกที่อักขระทั้งหมดถูกบังคับให้แสดงจากขวาไปซ้ายและ\u202Dซึ่งเป็นการแทนที่จากซ้ายไปขวาเริ่มบล็อกที่ซ้อนกันซึ่งทั้งหมด อักขระถูกบังคับให้อยู่ในลำดับจากซ้ายไปขวาแทนที่การแทนที่แรก

Ergo เมื่อมันแสดงรหัสต้นฉบับclass Mจะปรากฏขึ้นตามปกติ แต่\u202Eกลับลำดับการแสดงผลของทุกสิ่งจากที่นั่นไปที่\u202Dซึ่งกลับทุกอย่างอีกครั้ง (อย่างเป็นทางการทุกอย่างจาก\u202Dถึงจุดสิ้นสุดบรรทัดได้รับการกลับสองครั้งเนื่องจากครั้ง\u202Dและครั้งที่เหลือของข้อความที่กลับรายการเนื่องจาก\u202E, ซึ่งเป็นสาเหตุที่ข้อความนี้ปรากฏขึ้นในช่วงกลางของบรรทัดแทนปลาย) ทิศทางของบรรทัดถัดไปได้รับการจัดการอย่างเป็นอิสระจากอันแรกเนื่องจากตัวสิ้นสุดบรรทัดดังนั้น{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}จะแสดงตามปกติ

สำหรับเล่ม (ซับซ้อนมากหลายสิบหน้ายาว) Unicode อัลกอริทึมแบบสองทิศทางดูมาตรฐาน Unicode ภาคผนวก # 9


คุณไม่ได้อธิบายว่าคอมไพเลอร์ (ตรงข้ามกับรูทีนการแสดงผล) ทำอะไรกับอักขระ Unicode เหล่านั้นด้วยตนเอง ฉันอาจเพิกเฉยต่อพวกเขาทันที (หรือปฏิบัติต่อพวกเขาเป็นพื้นที่สีขาว) หรืออาจตีความว่าพวกเขามีส่วนร่วมในรหัสที่มาจริง ฉันไม่ทราบกฎ Java ที่นี่ แต่ความจริงที่ว่าพวกเขาถูกวางไว้ที่ส่วนท้ายของตัวระบุที่ไม่ได้ใช้แสดงให้ฉันเห็นว่าอาจเป็นตัวหลังและอักขระ Unicode เป็นส่วนหนึ่งของชื่อตัวระบุเหล่านั้น
Marc van Leeuwen

สิ่งนี้จะทำงานในลักษณะเดียวกันใน c # หรือไม่สนใจ?
IanF1

14
@ IanF1 มันจะทำงานในภาษาใดก็ได้ที่คอมไพเลอร์ / ล่ามนับตัวอักษร RTL และ LTR เป็นช่องว่าง แต่อย่าทำอย่างนี้ในรหัสการผลิตหากคุณเห็นคุณค่าของความมีสติของคนต่อไปที่จะแตะรหัสซึ่งอาจเป็นคุณได้
wizzwizz4

2
หรือกล่าวอีกนัยหนึ่งว่า: "ให้เขียนโค้ดเสมอว่าบุคคลที่ลงเอยด้วยการดูแลรหัสของคุณเป็นนักโรคจิตที่มีความรุนแรงซึ่งรู้ว่าคุณอยู่ที่ไหน" , @ IanF1 หรือบางที: "รหัสเสมอว่าคนที่จบการบำรุงรักษารหัสของคุณจะตั้งชื่อและอับอายคุณในฐานะผู้เขียนต้นฉบับในกองมากเกิน"
Cody Gray

43

เพราะมันมีลักษณะที่แตกต่างกันของUnicode สองทิศทางอัลกอริทึม มีอักขระสองตัวที่มองไม่เห็นของ RLO และ LRO ที่อัลกอริทึมแบบสองทิศทาง Unicode ใช้เพื่อเปลี่ยนลักษณะที่ปรากฏของอักขระที่ซ้อนกันระหว่างอักขระสองตัวนี้

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

หมายเหตุ 1: อัลกอริทึมนี้ใช้โดยโปรแกรมแก้ไขข้อความและเบราว์เซอร์เพื่อแสดงอักขระที่มองเห็นได้ทั้งตัวอักษร LTR (อังกฤษ) และ RTL (เช่นอารบิกฮิบรู) ในเวลาเดียวกัน - ด้วยเหตุนี้ "bi" -directional คุณสามารถอ่านเพิ่มเติมเกี่ยวกับขั้นตอนวิธีแบบสองทิศทางที่ Unicode ของเว็บไซต์
หมายเหตุ 2: พฤติกรรมที่แน่นอนของ LRO และ RLO ถูกกำหนดไว้ในส่วน 2.2ของอัลกอริทึม


ความสามารถดังกล่าวมีจุดประสงค์อะไร?
ยูจีน Sh.

6
บางครั้งจำเป็นต้องใช้อักขระเหล่านี้เพื่อแสดงภาพอาหรับและฮิบรูอย่างถูกต้อง ภาษาเหล่านี้จะอ่านและเขียนจากขวาไปซ้าย (RTL) ตัวอักษรตัวแรกที่มีการอ่าน / เขียนปรากฏบนด้านขวามือ คุณสามารถอ่านเพิ่มเติมที่นี่
James Lawson

อักขระอารบิกและฮิบรูนั้นอยู่ในตัว RTL แม้ว่ามันจะปรากฏ RTL แม้ว่าจะไม่มีการลบล้างอย่างชัดเจนก็ตามและพวกเขาจะย้อนลำดับการเรียงลำดับของตัวละครอื่นที่อยู่ใกล้เคียงโดยอัตโนมัติฉันคิดว่าเครื่องหมายวรรคตอนส่วนใหญ่
user2357112 รองรับ Monica

หน้านี้ที่นี่อธิบายเมื่อจำเป็นต้องแทนที่ @ user2357112 ถูกต้องพวกเขาไม่ค่อยต้องการ แน่นอนเมื่อคุณมีเครื่องหมายวรรคตอนใบเสนอราคาและตัวเลข - อักขระพิเศษเหล่านี้ถือเป็น "เป็นกลาง" สำหรับคอมพิวเตอร์ที่ไม่สามารถอ่านคำและเข้าใจบริบทก็ไม่มีความชัดเจนว่าจะรักษาพวกเขาเป็น LTR หรือ RTL แต่ขั้นตอนวิธีการ bidi ที่มีการเลือกบางส่วนการสั่งซื้อ บางครั้งมัน "ทำให้ผิด" และคุณต้องใช้อักขระแทนที่เหล่านี้เพื่อ "แก้ไข"
James Lawson

3
นอกจากนี้ U + 202E และ U + 202D ก็ไม่ถือว่าเป็นช่องว่าง Java จะพิจารณาพื้นที่ ASCII, แท็บแนวนอนฟีดรูปแบบและ CR / LF / CRLF เป็นช่องว่าง พวกเขากำลังจริง lexically ส่วนหนึ่งของตัวบ่งชี้M\u202Eและa\u202Dแต่ตัวบ่งชี้เหล่านั้นปรากฏที่จะถือว่าเป็นเทียบเท่ากับและM a(JLS ทำหน้าที่อธิบายได้ไม่ดี)
user2357112 รองรับ Monica

28

ตัวละครU+202Eสะท้อนรหัสจากขวาไปซ้ายมันฉลาดมาก ถูกซ่อนอยู่เริ่มต้นใน M

"class M\u202E{..."

ฉันพบความมหัศจรรย์หลังนี้ได้อย่างไร

ในตอนแรกเมื่อฉันเห็นคำถามที่ฉันแกร่ง "มันเป็นเรื่องตลกที่ทำให้คนอื่นเสียเวลา" แต่จากนั้นฉันก็เปิด IDE ของฉัน ("IntelliJ") สร้างคลาสและวางรหัส ... และมันก็รวบรวม !!! ดังนั้นฉันจึงดูดีขึ้นและเห็นว่า "โมฆะคงที่สาธารณะ" ย้อนหลังดังนั้นฉันจึงไปที่นั่นพร้อมเคอร์เซอร์และลบตัวอักษรสองสามตัว ... และเกิดอะไรขึ้น ตัวอักษรเริ่มลบย้อนหลังดังนั้นฉันคิดว่า mmm .... หายาก ... ฉันต้องรันมัน ... ดังนั้นฉันจะดำเนินการโปรแกรมต่อ แต่ก่อนอื่นฉันต้องบันทึก ... และนั่นคือตอนที่ฉัน พบมัน! . ฉันไม่สามารถบันทึกไฟล์ได้เนื่องจาก IDE ของฉันบอกว่ามีการเข้ารหัสที่แตกต่างกันสำหรับ char บางตัวและชี้ให้ฉันว่ามันอยู่ที่ไหนดังนั้นฉันจึงเริ่มการวิจัยใน Google เพื่อหาตัวอักษรพิเศษที่สามารถทำงานได้และนั่นคือ :)

เรื่องเล็กน้อย

อัลกอริทึมแบบสองทิศทาง Unicode และU+202Eเกี่ยวข้องอธิบายสั้น ๆ:

Unicode Standard กำหนดลำดับการแสดงหน่วยความจำที่เรียกว่าคำสั่งแบบลอจิคัล เมื่อข้อความแสดงเป็นเส้นแนวนอนสคริปต์ส่วนใหญ่จะแสดงอักขระจากซ้ายไปขวา อย่างไรก็ตามมีหลายสคริปต์ (เช่นภาษาอาหรับหรือภาษาฮิบรู) ที่มีการเรียงลำดับข้อความตามแนวนอนตามธรรมชาติจากขวาไปซ้าย หากข้อความทั้งหมดมีทิศทางแนวนอนที่สม่ำเสมอการเรียงลำดับของข้อความที่แสดงจะไม่ชัดเจน

อย่างไรก็ตามเนื่องจากสคริปต์จากขวาไปซ้ายเหล่านี้ใช้ตัวเลขที่เขียนจากซ้ายไปขวาข้อความจึงเป็นแบบสองทิศทาง: เป็นการผสมผสานระหว่างข้อความจากขวาไปซ้ายและซ้ายไปขวา นอกจากตัวเลขแล้วยังมีคำที่ฝังตัวจากภาษาอังกฤษและสคริปต์อื่น ๆ จากซ้ายไปขวารวมทั้งสร้างข้อความสองทิศทาง หากไม่มีข้อกำหนดที่ชัดเจนความคลุมเครือสามารถเกิดขึ้นในการกำหนดลำดับของอักขระที่แสดงเมื่อทิศทางแนวนอนของข้อความไม่สม่ำเสมอ

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

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

ทำไมสร้างขั้นตอนวิธีการบางอย่างเช่นนี้ ?

อัลกอริทึม bidi สามารถสร้างลำดับของอักขระอารบิกหรือฮีบรูหนึ่งหลังจากที่อื่นจากขวาไปซ้าย


4

บทที่ 3 ของข้อกำหนดภาษาให้คำอธิบายโดยอธิบายรายละเอียดวิธีการแปลคำศัพท์สำหรับโปรแกรม Java สิ่งที่สำคัญที่สุดสำหรับคำถาม:

โปรแกรมเขียนด้วย Unicode (§3.1)แต่มีการแปลคำศัพท์ (§3.2) เพื่อให้ Unicode หลุดรอด (§3.3) สามารถใช้เพื่อรวมอักขระ Unicode โดยใช้อักขระ ASCII เท่านั้น

ดังนั้นโปรแกรมจึงเขียนด้วยอักขระ Unicode และผู้เขียนสามารถหลบหนีได้โดยใช้\uxxxxในกรณีที่การเข้ารหัสไฟล์ไม่รองรับอักขระ Unicode ซึ่งในกรณีนี้โปรแกรมจะแปลเป็นอักขระที่เหมาะสม หนึ่งในตัวละคร Unicode \u202Eปัจจุบันในกรณีนี้คือ มันไม่ได้ปรากฏให้เห็นในตัวอย่าง แต่ถ้าคุณลองเปลี่ยนการเข้ารหัสของเบราว์เซอร์ตัวละครที่ซ่อนอยู่อาจปรากฏขึ้น

ดังนั้นการแปลคำศัพท์ในการประกาศคลาส:

class M\u202E{

M\u202Eซึ่งหมายความว่ารหัสระดับชั้นคือ ข้อกำหนดนี้ถือว่าเป็นตัวบ่งชี้ที่ถูกต้อง:

Identifier:
    IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
    JavaLetter {JavaLetterOrDigit}

"ตัวอักษรหรือตัวเลข Java" เป็นอักขระที่เมธอดCharacter.isJavaIdentifierPart(int)ส่งคืนค่าจริง


ขออภัยที่นี่ย้อนหลัง (เล่นสำนวนเจตนา) ไม่มีการยกเว้นในซอร์สโค้ด คุณกำลังอธิบายว่ามันสามารถเขียนได้อย่างไร และมันจะรวมเข้ากับคลาสที่ชื่อว่า "M" (เพียงหนึ่งตัวอักษร)
Tom Blodget

@ TomBlodget แน่นอน แต่จุด (ซึ่งในความเป็นจริงฉันเน้นในใบเสนอราคา spec) คือการรวบรวมสามารถประมวลผลอักขระ Unicode ดิบ นั่นคือคำอธิบายทั้งหมด การแปลแบบ Escape เป็นเพียงข้อมูลเพิ่มเติมและไม่เกี่ยวข้องโดยตรงกับกรณีนี้ สำหรับคลาสที่คอมไพล์ฉันคิดว่าเป็นเพราะอักขระสวิตช์ RTL นั้นถูกคอมไพเลอร์ทิ้งไป ฉันจะพยายามดูว่าสิ่งนี้คาดหวังหรือไม่ แต่ฉันคิดว่าจะเกิดขึ้นหลังจากช่วงการแปลคำศัพท์
M Anouti
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.