ทำไมชื่อ charset ไม่ใช่ค่าคงที่?


211

ปัญหาชุดอักขระนั้นสร้างความสับสนและซับซ้อนด้วยตัวเอง แต่นอกเหนือจากนั้นคุณต้องจำชื่อที่แน่นอนของชุดอักขระของคุณ มันคือ"utf8"อะไร หรือ"utf-8"? หรืออาจจะ"UTF-8"? เมื่อค้นหาตัวอย่างรหัสอินเทอร์เน็ตคุณจะเห็นข้อมูลทั้งหมดข้างต้น ทำไมไม่เพียงทำให้พวกเขาตั้งชื่อค่าคงที่และใช้Charset.UTF8?


19
+1: สิ่งนี้ก็ทำให้ฉันตลอดเวลาด้วย เรื่องราวเดียวกันดำเนินต่อไปMessageDigest#getInstance()โดยวิธี
BalusC

2
สำหรับคำตอบที่แท้จริงคุณต้องถามใครสักคนที่ซัน โชคดีกับการที่ :-)
สตีเฟ่นซี

1
Stephen C: ฉันเชื่อว่ามีการพูดคุยกันในรายชื่อรับเมลสาธารณะ - คนที่ดวงอาทิตย์
Tom Hawtin - tackline

คำตอบ:


160

คำตอบสำหรับคำถามที่ถามง่าย ๆ คือสตริง charset ที่มีให้ใช้นั้นแตกต่างกันไปในแต่ละแพลตฟอร์ม

อย่างไรก็ตามมีหกสิ่งที่จำเป็นต้องมีอยู่ดังนั้นค่าคงที่อาจถูกสร้างขึ้นมานานแล้ว ฉันไม่รู้ว่าทำไมพวกเขาถึงไม่ได้

JDK 1.4 ทำได้ดีมากโดยแนะนำประเภท Charset ณ จุดนี้พวกเขาไม่ต้องการให้ค่าคงที่สตริงอีกต่อไปเนื่องจากเป้าหมายคือให้ทุกคนใช้อินสแตนซ์ Charset ดังนั้นทำไมไม่จัดให้มีค่าคงที่มาตรฐาน Charset ทั้งหกอยู่ล่ะ ฉันถาม Martin Buchholz เนื่องจากเขากำลังนั่งอยู่ถัดจากฉันและเขาบอกว่าไม่มีเหตุผลที่ยอดเยี่ยมจริงๆยกเว้นในเวลานั้นสิ่งต่าง ๆ ยังคงมีความอ่อนไหว - JDK APIs จำนวนน้อยเกินไปได้รับการดัดแปลง ยอมรับ Charset และสิ่งที่เคยเป็น Charset เกินพิกัดมักจะดำเนินการที่เลวร้ายยิ่งขึ้นเล็กน้อย

เป็นเรื่องน่าเศร้าที่ใน JDK 1.6 นั้นในที่สุดพวกเขาก็เสร็จเรียบร้อยทุกอย่างด้วย Charset เกินพิกัด และสถานการณ์การทำงานแบบย้อนหลังนี้ยังคงมีอยู่ (เหตุผลที่แปลกอย่างไม่น่าเชื่อและฉันไม่สามารถอธิบายได้ แต่เกี่ยวข้องกับความปลอดภัย!)

เรื่องสั้นสั้น - เพียงแค่กำหนดค่าคงที่ของคุณเองหรือใช้คลาส Charsets ของ Guava ที่ Tony the Pony เชื่อมโยงกับ (แม้ว่าห้องสมุดนั้นยังไม่ได้เปิดตัวจริง)

ปรับปรุง:StandardCharsetsชั้นอยู่ใน JDK 7


แค่อยากรู้อยากเห็นความคิดใด ๆ เมื่อจะมีการเปิดตัว (อัลฟา / เบต้า / อะไรก็ตาม) ของ Guava? หน้าแรกของโครงการเป็นบิตสั้น ๆ เกี่ยวกับเรื่องนี้
Jonik

ไม่มีไก่งวงสำหรับฉันจนกว่ามันจะออก!
เควิน Bourrillion

เหตุผลที่แปลกอย่างไม่น่าเชื่อและฉันไม่สามารถอธิบายได้ แต่เกี่ยวข้องกับความปลอดภัย - คุณสามารถสร้าง String ที่แก้ไขได้ผ่านชุดอักขระที่กำหนดเอง แต่พวกเขาก็สามารถทำงานได้เร็วกว่าสตริง มันเป็นการละเลย / ละเลยวิธีString(byte bytes[], int offset, int length, Charset charset)การใช้งาน อันที่จริงแล้วประสิทธิภาพการทำงานนั้นไม่สำคัญเลยเมื่อสร้างสตริงขนาดเล็กจากไบต์ขนาดใหญ่ []
bestsss

7
ไม่ยุติธรรม! คุณสามารถเข้าถึงทรัพยากรที่ยอดเยี่ยมเช่นนั้น = (ฉันเห็นคำตอบอื่นที่คุณเคยพูดว่า "ใช่ฉันถาม Josh [Bloch] เกี่ยวกับเรื่องนั้น ... "
kevinarpe

PrintStream ไม่รองรับ Charset
rofrol

102

สองปีต่อมาและตอนนี้StandardCharsetsของ Java 7 กำหนดค่าคงที่สำหรับ 6 ชุดอักขระมาตรฐาน

หากคุณกำลังติดอยู่บน Java 5/6 คุณสามารถใช้ฝรั่งของชุดอักขระค่าคงที่ตามที่แนะนำโดยเควิน Bourrillion และจอน Skeet


29

ฉันยืนยันว่าเราสามารถทำได้ดีกว่านั้น ... ทำไมจึงไม่สามารถเข้าถึงชุดอักขระที่รับประกันได้โดยตรง Charset.UTF8ควรอ้างอิงถึงCharsetไม่ใช่ชื่อเป็นสตริง ด้วยวิธีนี้เราจะไม่ต้องจัดการUnsupportedEncodingExceptionกับสถานที่ทั้งหมด

โปรดทราบว่าฉันคิดว่า. NET เลือกกลยุทธ์ที่ดีกว่าโดยเริ่มต้นที่ UTF-8 ในทุกที่ จากนั้นจะทำให้เมาด้วยการตั้งชื่อคุณสมบัติการเข้ารหัส "ค่าเริ่มต้นของระบบปฏิบัติการ"Encoding.Defaultซึ่งไม่ใช่ค่าเริ่มต้นภายใน. NET ของตัวเอง :(

กลับไปพูดจาโผงผางเกี่ยวกับการสนับสนุนชุดอักขระของ Java - ทำไมไม่มีคอนสตรัคเตอร์สำหรับFileWriter/ FileReaderที่ใช้Charset ? โดยทั่วไปนั่นเป็นคลาสที่เกือบไร้ประโยชน์เนื่องจากข้อ จำกัด นั้น - คุณเกือบจะต้องมีInputStreamReaderรอบFileInputStreamหรือเทียบเท่าสำหรับเอาต์พุต :(

พยาบาล, พยาบาล - ยาของฉันอยู่ที่ไหน

แก้ไข: มันเกิดขึ้นกับฉันว่านี่ไม่ได้ตอบคำถาม คำตอบที่แท้จริงน่าจะเป็น "ไม่มีใครเกี่ยวข้องกับความคิดของมัน" หรือ "ใครบางคนที่เกี่ยวข้องคิดว่ามันเป็นความคิดที่ไม่ดี" ฉันจะขอแนะนำว่าในบ้านอาคารเรียนให้ชื่อหรือทำสำเนาชุดอักขระหลีกเลี่ยงรอบ codebase ... หรือคุณก็สามารถใช้อย่างใดอย่างหนึ่งที่เรานำมาใช้ใน Google เมื่อคำตอบนี้ถูกเขียนขึ้นเป็นครั้งแรก (โปรดทราบว่าตั้งแต่ Java 7 คุณจะใช้StandardCharsetsแทน)


2
+1 แต่เป็นวิธีการมากกว่าเขตข้อมูลเพื่อให้การโหลดแบบขี้เกียจ (โอเคคุณอาจต้องการ UTF-8 แต่มีชุดอักขระอื่น ๆ อยู่สองสามชุดและคุณอาจต้องการสิ่งอำนวยความสะดวกที่คล้ายกัน) น่าเสียดายที่เรื่องนี้ดูเหมือนจะไม่เป็นที่นิยมมากกับผู้ที่ตัดสินใจ
Tom Hawtin - tackline

ฉันมีความสุขมากกับวิธีการแม้ว่าฉันหวังว่าการโหลดชุดอักขระจำนวนเล็กน้อยเหล่านั้นอย่างกระตือรือร้นนั้นจะไม่คุ้มค่ามากนัก
Jon Skeet

1
เรากำลังอยู่ในสงครามครูเสดเพื่อหยุดการโหลดในชั้นเรียนที่กระตือรือร้น / เพิ่งทำการค้นหา JDK สำหรับ "UTF-8" พบ 270 การแข่งขัน (es) ใน 165 ไฟล์ แม้ว่าส่วนมากจะเป็นขยะ Apache เก่า (ฉันเชื่อว่ามีส่วนร่วมโดยทีมงานของฉัน)
Tom Hawtin - tackline

1
@tackline: ฉันคิดว่าการจัดหมวดหมู่ความกระตือรือร้นเป็นหนึ่งในสิ่งเหล่านั้นที่เมานท์ตลอดเวลา ไม่กี่ชั้นเรียนที่นี่ไม่กี่ชั้นเรียนที่นั่นแต่ละคนฟังดูไร้เดียงสาพอจะสร้างความแตกต่างที่ยิ่งใหญ่
Jon Skeet

ลิงก์สุดท้ายไปยัง Guava Charsets เสีย
LarsH


5

สถานะปัจจุบันของการเข้ารหัส API ทำให้บางสิ่งเป็นที่ต้องการ บางส่วนของ Java 6 API ไม่ยอมรับCharsetในสถานที่ของสตริง (ในlogging, dom.ls,PrintStream ; อาจจะมีคนอื่น ๆ ) มันไม่ได้ช่วยให้การเข้ารหัสควรมีชื่อบัญญัติที่แตกต่างกันสำหรับส่วนต่าง ๆ ของไลบรารีมาตรฐาน

ฉันเข้าใจได้ว่าสิ่งต่าง ๆ เกิดขึ้นได้อย่างไร ไม่แน่ใจว่าฉันมีความคิดที่ยอดเยี่ยมเกี่ยวกับวิธีการแก้ไข


ในฐานะที่เป็นกัน ...

คุณสามารถค้นหาชื่อสำหรับการใช้งาน Java 6 ของ Sun ได้ที่นี่ที่นี่

สำหรับ UTF-8, ค่าที่ยอมรับเป็น"UTF-8"สำหรับjava.nioและ"UTF8"สำหรับและjava.lang java.ioการเข้ารหัสเฉพาะข้อมูลจำเพาะนั้นต้องการ JRE เพื่อสนับสนุนคือ: US-ASCII; ISO-8859-1; UTF-8; UTF-16BE; UTF-16LE; UTF-16


2
ฉันไม่ได้บ่นกับ PrintStream เนื่องจากคลาสพูดอย่างชัดเจนว่า "ควรใช้คลาส PrintWriter ในสถานการณ์ที่ต้องใช้อักขระเขียนแทนไบต์" (ซึ่งก็คือทุกสถานการณ์ ... )
Kevin Bourrillion

2

ฉันได้กำหนดคลาสยูทิลิตี้เมื่อนานมาแล้วด้วยค่าคงที่ UTF_8, ISO_8859_1 และ US_ASCII

นอกจากนี้บางเวลานานที่ผ่านมา (2 + ปี) ผมได้ทดสอบประสิทธิภาพการทำงานที่เรียบง่ายระหว่างnew String( byte[], Charset )และnew String( byte[], String charset_name )และพบว่าการดำเนินงานหลังเป็นอย่างมากได้เร็วขึ้น หากคุณดูที่ซอร์สโค้ดคุณจะเห็นว่าพวกเขาติดตามเส้นทางที่แตกต่างกัน

ด้วยเหตุนี้ฉันจึงรวมโปรแกรมอรรถประโยชน์ไว้ในคลาสเดียวกัน

public static String stringFromByteArray (
    final byte[] array,
    final Charset charset
)
{
    try
    {
        return new String( array, charset.name( ) )
    }
    catch ( UnsupportedEncodingException ex )
    {
        // cannot happen
    }
}

ทำไมตัวสร้างสตริง (byte [], Charset) จึงไม่เหมือนกัน


1
Charsetไม่จำเป็นที่จะต้องได้รับการจดทะเบียนเพื่อยกเว้นสามารถเกิดขึ้นได้ IIRC มีการเปลี่ยนแปลงบางอย่างใน JDK7 เพื่อให้เร็วขึ้นสำหรับCharsetการใช้งานที่รู้จักดี(กำจัดสำเนาเพิ่มเติม)
Tom Hawtin - tackline
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.