1. กระบวนการแปลงการเข้ารหัส Java
เรามักจะใช้ไฟล์คลาส Java เพื่อโต้ตอบกับผู้ใช้โดยตรง (อินพุต, เอาต์พุต) และข้อความที่มีอยู่ในเนื้อหาแบบโต้ตอบเหล่านี้อาจมีภาษาจีน ไม่ว่าจะเป็นคลาส Java เหล่านี้โต้ตอบกับฐานข้อมูลหรือกับหน้าส่วนหน้าวงจรชีวิตของพวกเขามักจะเป็นเช่นนี้:
(1) โปรแกรมเมอร์เขียนรหัสโปรแกรมผ่านตัวแก้ไขในระบบปฏิบัติการและบันทึกระบบปฏิบัติการในรูปแบบของ. Java เราเรียกไฟล์เหล่านี้ว่าไฟล์ต้นฉบับ
(2) รวบรวมไฟล์ต้นฉบับเหล่านี้ผ่าน javac.exe ใน JDK เพื่อสร้างคลาส. class
(3) เรียกใช้คลาสเหล่านี้โดยตรงหรือปรับใช้ในเว็บคอนเทนเนอร์เพื่อรับผลลัพธ์ผลลัพธ์
กระบวนการเหล่านี้สังเกตได้จากมุมมองของแมโครและไม่สามารถเข้าใจสิ่งนี้ได้อย่างแน่นอน เราจำเป็นต้องเข้าใจอย่างแท้จริงว่า Java ถูกเข้ารหัสและถอดรหัสอย่างไร:
ขั้นตอนที่ 1: เมื่อเราใช้ตัวแก้ไขเพื่อเขียนไฟล์แหล่งข้อมูล Java ไฟล์โปรแกรมจะใช้รูปแบบการเข้ารหัสเริ่มต้นของระบบปฏิบัติการ (โดยปกติแล้วระบบปฏิบัติการจีนของเราใช้รูปแบบการเข้ารหัส GBK) เพื่อสร้างไฟล์. Java ไฟล์ต้นฉบับ Java ถูกบันทึกไว้ในรูปแบบการเข้ารหัสไฟล์การเข้ารหัสที่สนับสนุนโดยระบบปฏิบัติการโดยค่าเริ่มต้น รหัสต่อไปนี้สามารถดูค่าพารามิเตอร์การเข้ารหัสไฟล์ของระบบ
System.out.println (System.getProperty ("file.encoding")); ขั้นตอนที่ 2: เมื่อเราใช้ Javac.exe เพื่อรวบรวมไฟล์ Java ของเรา JDK จะยืนยันการเข้ารหัสพารามิเตอร์การรวบรวมเพื่อกำหนดชุดอักขระซอร์สโค้ด หากเราไม่ได้ระบุพารามิเตอร์การรวบรวม JDK จะได้รับไฟล์เริ่มต้นของระบบปฏิบัติการการเข้ารหัสพารามิเตอร์ก่อนแล้ว JDK จะแปลงโปรแกรมแหล่ง Java ที่เราเขียนจากไฟล์การเข้ารหัสไฟล์การเข้ารหัสเป็นรูปแบบ unicode เริ่มต้นภายใน Java และใส่ไว้ในหน่วยความจำ
ขั้นตอนที่ 3: JDK เขียนข้อมูลที่รวบรวมและบันทึกไว้ในหน่วยความจำด้านบนลงในไฟล์คลาสเพื่อสร้างไฟล์. class ในเวลานี้ไฟล์. class คือการเข้ารหัส Unicode ซึ่งหมายความว่าเนื้อหาในไฟล์. class ทั่วไปของเราจะถูกแปลงเป็นรูปแบบการเข้ารหัส Unicode ไม่ว่าจะเป็นอักขระภาษาจีนหรือภาษาอังกฤษ
ในขั้นตอนนี้วิธีการจัดการไฟล์แหล่งที่มาของ JSP นั้นแตกต่างกันเล็กน้อย: เว็บคอนเทนเนอร์เรียกคอมไพเลอร์ JSP คอมไพเลอร์ JSP จะตรวจสอบก่อนว่าไฟล์ JSP มีรูปแบบการเข้ารหัสไฟล์หรือไม่ หากไม่ได้ตั้งค่าคอมไพเลอร์ JSP จะเรียก JDK เพื่อแปลงไฟล์ JSP เป็นคลาส servlet ชั่วคราวโดยใช้วิธีการเข้ารหัสเริ่มต้นจากนั้นรวบรวมเป็นไฟล์. class และเก็บไว้ในโฟลเดอร์ชั่วคราว
ขั้นตอนที่ 4: เรียกใช้คลาสที่รวบรวม: จะมีหลายสถานการณ์ที่นี่
(1) ทำงานโดยตรงบนคอนโซล
(2) คลาส JSP/Servlet
(3) ระหว่างคลาส Java และฐานข้อมูล
แต่ละสถานการณ์ทั้งสามนี้จะมีวิธีที่แตกต่างกันในการทำ
1. คลาสที่ทำงานบนคอนโซล
ในกรณีนี้ JVM จะอ่านไฟล์คลาสที่บันทึกไว้ในระบบปฏิบัติการลงในหน่วยความจำก่อน ในเวลานี้ไฟล์คลาสในหน่วยความจำจะถูกเข้ารหัสใน Unicode จากนั้น JVM จะเรียกใช้ หากผู้ใช้ต้องการป้อนข้อมูลการป้อนข้อมูลโดยผู้ใช้จะถูกเข้ารหัสในรูปแบบไฟล์การเข้ารหัสและแปลงเป็นรูปแบบการเข้ารหัส Unicode เพื่อบันทึกในหน่วยความจำ หลังจากรันโปรแกรมผลลัพธ์จะถูกแปลงเป็นรูปแบบไฟล์การเข้ารหัสและส่งกลับไปยังระบบปฏิบัติการและเอาต์พุตไปยังอินเตอร์เฟส กระบวนการทั้งหมดมีดังนี้:
ในกระบวนการทั้งหมดข้างต้นไม่มีข้อผิดพลาดใด ๆ เกิดขึ้นในการแปลงการเข้ารหัสที่เกี่ยวข้องมิฉะนั้นรหัสที่อ่านไม่ออกจะเกิดขึ้น
2. ชั้นเรียน
เนื่องจากไฟล์ JSP จะถูกแปลงเป็นไฟล์ servlet ในที่สุด (แต่ตำแหน่งที่เก็บข้อมูลแตกต่างกัน) เราจะรวมไฟล์ JSP ที่นี่ด้วย
เมื่อผู้ใช้ร้องขอ servlet เว็บคอนเทนเนอร์จะเรียก JVM เพื่อเรียกใช้ servlet ก่อนอื่น JVM จะโหลดคลาส servlet ลงในหน่วยความจำ รหัส servlet ในหน่วยความจำอยู่ในรูปแบบการเข้ารหัส Unicode จากนั้น JVM จะรัน servlet ในหน่วยความจำ ในระหว่างการรันหากคุณต้องการรับข้อมูลที่ส่งผ่านจากไคลเอนต์ (เช่นข้อมูลที่ส่งผ่านตามแบบฟอร์มและ URL) เว็บคอนเทนเนอร์จะยอมรับข้อมูลที่เข้ามา ในระหว่างกระบวนการรับสัญญาณหากโปรแกรมตั้งค่าการเข้ารหัสพารามิเตอร์ขาเข้ารูปแบบการเข้ารหัสชุดจะถูกนำมาใช้ หากไม่ได้ตั้งค่ารูปแบบการเข้ารหัส ISO-8859-1 เริ่มต้นจะถูกนำมาใช้ หลังจากข้อมูลที่ได้รับ JVM จะแปลงรูปแบบการเข้ารหัสเป็น Unicode และเก็บไว้ในหน่วยความจำ หลังจากเรียกใช้ servlet ผลลัพธ์ผลลัพธ์จะถูกสร้างขึ้นและรูปแบบการเข้ารหัสของผลลัพธ์เอาต์พุตเหล่านี้ยังคงเป็น Unicode ทันทีหลังจากนั้นเว็บคอนเทนเนอร์จะส่งสตริงรูปแบบการเข้ารหัส Unicode ที่สร้างขึ้นโดยตรงไปยังไคลเอนต์ หากโปรแกรมระบุรูปแบบการเข้ารหัส ณ เวลาที่ส่งออกมันจะเป็นเอาต์พุตไปยังเบราว์เซอร์ตามรูปแบบการเข้ารหัสที่ระบุ มิฉะนั้นรูปแบบการเข้ารหัส ISO-8859-1 เริ่มต้นจะถูกนำมาใช้ แผนภูมิการไหลของกระบวนการทั้งหมดมีดังนี้:
3. ส่วนฐานข้อมูล
เรารู้ว่าการเชื่อมต่อระหว่างโปรแกรม Java และฐานข้อมูลเชื่อมต่อผ่านไดรเวอร์ JDBC และไดรเวอร์ JDBC เริ่มต้นกับรูปแบบการเข้ารหัส ISO-8859-1 กล่าวคือเมื่อเราส่งข้อมูลไปยังฐานข้อมูลผ่านโปรแกรม Java JDBC จะแปลงข้อมูลในรูปแบบการเข้ารหัส Unicode เป็นครั้งแรกในรูปแบบการเข้ารหัส ISO-8859-1 จากนั้นจัดเก็บไว้ในฐานข้อมูลนั่นคือเมื่อฐานข้อมูลบันทึกข้อมูลรูปแบบเริ่มต้น ISO-8859-1
2. การเข้ารหัสและการถอดรหัส
ต่อไปนี้จะสิ้นสุดการเข้ารหัสและการถอดรหัสที่ Java จำเป็นต้องดำเนินการในโอกาสเหล่านั้นและจัดเรียงกระบวนการขั้นกลางโดยละเอียดเพื่อให้หลักกระบวนการเข้ารหัสและการถอดรหัสของ Java มีสี่สถานการณ์หลักใน Java ที่ต้องใช้การเข้ารหัสและถอดรหัส:
(1): การดำเนินการ I/O
(2): หน่วยความจำ
(3): ฐานข้อมูล
(4): Javaweb
ต่อไปนี้ส่วนใหญ่แนะนำสถานการณ์สองสถานการณ์ก่อนหน้า ตราบใดที่ตั้งฐานข้อมูลอย่างถูกต้องจะไม่มีปัญหา มีสถานการณ์ Javaweb มากเกินไปและคุณต้องเข้าใจการเข้ารหัส URL, Get, Post และ Servlet Decoding ดังนั้นการแนะนำ LZ ของสถานการณ์ Javaweb
1.I/o การดำเนินการ
ใน LZ ก่อนหน้านี้กล่าวว่าปัญหาที่อ่านไม่ออกไม่มีอะไรมากไปกว่าความไม่สอดคล้องกันของรูปแบบการเข้ารหัสในระหว่างกระบวนการแปลงรหัส ตัวอย่างเช่น UTF-8 ใช้สำหรับการเข้ารหัสและใช้ GBK สำหรับการถอดรหัส อย่างไรก็ตามเหตุผลพื้นฐานที่สุดคือมีปัญหาเกี่ยวกับการแปลงตัวละครเป็นไบต์หรือไบต์เป็นตัวละครและสถานการณ์หลักของการแปลงในกรณีนี้คือการดำเนินการ I/O แน่นอนว่าการดำเนินงาน I/O ส่วนใหญ่รวมถึงเครือข่าย I/O (นั่นคือ Javaweb) และดิสก์ I/O เครือข่าย I/O ถูกนำมาใช้ในส่วนถัดไป
ก่อนอื่นมาดูการดำเนินการเข้ารหัส I/O
InputStream เป็น superclass ของคลาสทั้งหมดของสตรีมอินพุตไบต์และผู้อ่านเป็นคลาสนามธรรมของผู้อ่าน Java อ่านไฟล์ในลักษณะที่แบ่งออกเป็นสตรีมไบต์และโดยสตรีมอักขระ InputStream และ Reader เป็น superclasses ของวิธีการอ่านทั้งสองนี้
โดยไบต์เรามักจะใช้เมธอด InputStream.read () เพื่ออ่านไบต์ในสตรีมข้อมูล (อ่าน () อ่านครั้งละหนึ่งไบต์ซึ่งช้ามากเรามักจะใช้การอ่าน (ไบต์ [])) จากนั้นบันทึกไว้ในไบต์ [] อาเรย์และในที่สุดก็แปลงเป็นสตริง เมื่อเราอ่านไฟล์การเข้ารหัสของไบต์ขึ้นอยู่กับรูปแบบการเข้ารหัสที่ใช้โดยไฟล์และปัญหาการเข้ารหัสจะมีส่วนร่วมในการแปลงเป็นกระบวนการสตริง หากรูปแบบการเข้ารหัสแตกต่างกันระหว่างทั้งสองปัญหาอาจเกิดขึ้น ตัวอย่างเช่นมีปัญหาที่รูปแบบการเข้ารหัส TXT คือ UTF-8 ดังนั้นรูปแบบการเข้ารหัสข้อมูลสตรีมข้อมูลที่ได้รับเมื่ออ่านไฟล์ผ่านสตรีมไบต์คือ UTF-8 หากเราไม่ได้ระบุรูปแบบการเข้ารหัสระหว่างการแปลงเป็นสตริงเราจะใช้รูปแบบการเข้ารหัสระบบ (GBK) โดยค่าเริ่มต้นเป็นการถอดรหัส เนื่องจากรูปแบบการเข้ารหัสของทั้งสองไม่สอดคล้องกันรหัสที่อ่านไม่ออกจะเกิดขึ้นอย่างแน่นอนในกระบวนการสร้างสตริงดังนี้:
ไฟล์ไฟล์ = ไฟล์ใหม่ ("c: //test.txt"); inputStream input = new FileInputStream (ไฟล์); StringBuffer buffer = new StringBuffer (); ไบต์ [] ไบต์ = ไบต์ใหม่ [1024]; สำหรับ (int n; (n = input.read (bytes))! =-1;) {buffer.append (สตริงใหม่ (ไบต์, 0, n)); } system.out.println (บัฟเฟอร์); ผลลัพธ์ผลลัพธ์ถูกอ่านไม่ออก ...
เนื้อหาใน test.txt คือ: ฉันเป็น CM
เพื่อหลีกเลี่ยงรหัสที่อ่านไม่ออกให้ระบุรูปแบบการเข้ารหัสในระหว่างกระบวนการก่อสร้างสตริงเพื่อให้รูปแบบการเข้ารหัสของทั้งสองสอดคล้องกันระหว่างการเข้ารหัสและการถอดรหัส:
buffer.append (สตริงใหม่ (ไบต์, 0, n, "UTF-8"));
โดยอักขระสตรีมอักขระสามารถถือได้ว่าเป็นสตรีม wrapper เลเยอร์พื้นฐานของมันยังคงใช้สตรีมไบต์เพื่ออ่านไบต์และจากนั้นจะถอดรหัสไบต์อ่านเป็นอักขระโดยใช้วิธีการเข้ารหัสที่ระบุ ใน Java ผู้อ่านเป็น superclass ที่อ่านสตรีมอักขระ ดังนั้นจากมุมมองด้านล่างไม่มีความแตกต่างระหว่างการอ่านไฟล์โดยไบต์และการอ่านโดยตัวละคร เมื่ออ่านการอ่านตัวละครจะถูกทิ้งไว้กับไบต์ในแต่ละครั้งและสตรีมไบต์อ่านหนึ่งไบต์ในแต่ละครั้ง
BYTE & การแปลงอักขระแปลงไบต์เป็นอักขระเป็นหลัก inputStreamReader API มีการอธิบายดังนี้: InputStreamReader เป็นบริดจ์ระหว่างสตรีมไบต์ไปยังสตรีมอักขระ: มันอ่านไบต์โดยใช้ charset ที่ระบุและถอดรหัสเป็นอักขระ ชุดอักขระที่ใช้สามารถระบุหรือระบุอย่างชัดเจนโดยชื่อหรือสามารถยอมรับชุดอักขระเริ่มต้นของแพลตฟอร์ม การโทรแต่ละครั้งไปยังวิธีการอ่าน () ในอินพุทสตรีมรีดจะส่งผลให้หนึ่งไบต์หรือมากกว่านั้นถูกอ่านจากสตรีมอินพุตพื้นฐาน เพื่อเปิดใช้งานการแปลงที่มีประสิทธิภาพจากไบต์เป็นอักขระคุณสามารถอ่านไบต์เพิ่มเติมจากกระแสข้อมูลล่วงหน้าเกินกว่าไบต์ที่จำเป็นเพื่อตอบสนองการดำเนินการอ่านปัจจุบัน คำอธิบาย API นั้นชัดเจนมาก InputStreamReader ยังคงใช้การอ่านไบต์เมื่ออ่านไฟล์ที่ด้านล่าง หลังจากอ่านไบต์มันจะต้องแยกวิเคราะห์เป็นตัวละครตามรูปแบบการเข้ารหัสที่ระบุ หากไม่มีรูปแบบการเข้ารหัสที่ระบุจะใช้รูปแบบการเข้ารหัสเริ่มต้นของระบบ
String file = "C: //test.txt"; String charset = "UTF-8"; // เขียนอักขระเพื่อแปลงเป็น BYTE Stream FileOutputStream OutputStream = ใหม่ fileOutputStream (ไฟล์); OutputStreamWriter Writer = New OutputStreamWriter (OutputStream, Charset); ลอง {writer.write ("ฉันเป็น cm"); } ในที่สุด {writer.close (); } // อ่าน bytes เพื่อแปลงเป็นอักขระ fileInputStream inputStream = ใหม่ fileInputStream (ไฟล์); inputStreamReader reader = new InputStreamReader (inputStream, charset); StringBuffer buffer = new StringBuffer (); ถ่าน [] buf = ถ่านใหม่ [64]; จำนวน int = 0; ลอง {ในขณะที่ ((count = reader.read (buf))! = -1) {buffer.append (buf, 0, count); }} ในที่สุด {reader.close (); } system.out.println (บัฟเฟอร์); 2. หน่วยความจำ
ก่อนอื่นมาดูรหัสง่าย ๆ ต่อไปนี้
สตริง s = "ฉันเป็น cm"; ไบต์ [] ไบต์ = s.getBytes (); สตริง s1 = สตริงใหม่ (ไบต์, "gbk"); สตริง s2 = สตริงใหม่ (ไบต์);
ในรหัสนี้เราจะเห็นกระบวนการแปลงการเข้ารหัสสามกระบวนการ (การเข้ารหัสหนึ่งครั้งการถอดรหัสสองครั้ง) มาดู String.getTytes () ก่อน:
ไบต์สาธารณะ [] getBytes () {return stringCoding.encode (ค่า, 0, value.length); -เรียกใช้วิธีการ stringcoding.encode () ภายใน:
byte แบบคงที่ [] encode (char [] ca, int ปิด, int len) {string csn = charset.defaultCharset (). name (); ลอง {// ใช้ตัวแปรชื่อ charset encode () ซึ่งให้แคช return encode (CSN, CA, OFF, LEN); } catch (unsupportencodingexception x) {warnunsupportedCharset (CSN); } ลอง {return encode ("ISO-8859-1", CA, OFF, LEN); } catch (unsupportencodingexception x) {// ถ้ารหัสนี้ถูกตีในระหว่างการเริ่มต้น VM ข้อความคือ // วิธีเดียวที่เราจะได้รับข้อความแสดงข้อผิดพลาดใด ๆ MessageUtils.err ("ISO-8859-1 Charset ไม่สามารถใช้ได้:" + X.ToString ()); // หากเราไม่สามารถหา ISO-8859-1 (การเข้ารหัสที่จำเป็น) แล้วสิ่งต่าง ๆ // นั้นผิดอย่างจริงจังกับการติดตั้ง System.Exit (1); คืนค่า null; -การเข้ารหัส (char [] paramarrayofchar, int paramint1, int paramint2) วิธีแรกเรียกรูปแบบการเข้ารหัสเริ่มต้นของระบบ หากไม่ได้ระบุรูปแบบการเข้ารหัสการดำเนินการเข้ารหัสจะดำเนินการโดยค่าเริ่มต้นโดยใช้รูปแบบการเข้ารหัส ISO-8859-1 การลึกขึ้นไปมีดังนี้:
String csn = (charsetName == null)? "ISO-8859-1": CharsetName;
ในวิธีการเดียวกันคุณจะเห็นว่าตัวสร้างของสตริงใหม่เรียกว่าเมธอด stringCoding.decode ():
สตริงสาธารณะ (ไบต์ไบต์ [], ออฟเซ็ต int, ความยาว int, charset charset) {ถ้า (charset == null) โยน nullpointerexception ใหม่ ("charset"); Checkbounds (ไบต์, ชดเชย, ความยาว); this.value = stringCoding.decode (charset, ไบต์, ออฟเซ็ต, ความยาว); - วิธีการถอดรหัสและเข้ารหัสจัดการรูปแบบการเข้ารหัสด้วยวิธีเดียวกัน
สำหรับสองสถานการณ์ข้างต้นเราจำเป็นต้องตั้งค่ารูปแบบการเข้ารหัสแบบครบวงจรโดยทั่วไปจะไม่มีปัญหาที่อ่านไม่ออก
3. รูปแบบการเข้ารหัสและการเข้ารหัส
ก่อนอื่นให้ดูแผนภาพคลาสการเข้ารหัส Java
ขั้นแรกให้ตั้งค่าคลาส Chartset ตามแผนภูมิที่ระบุจากนั้นสร้างวัตถุ ChartsetEncoder ตาม Chartset และในที่สุดเรียกใช้ Charsetencoder.encode เพื่อเข้ารหัสสตริง ประเภทการเข้ารหัสที่แตกต่างกันจะสอดคล้องกับคลาสและกระบวนการเข้ารหัสจริงเสร็จสมบูรณ์ในคลาสเหล่านี้ แผนภาพเวลาต่อไปนี้แสดงกระบวนการเข้ารหัสโดยละเอียด:
ผ่านแผนภาพคลาสที่เข้ารหัสและแผนภาพเวลานี้คุณสามารถเข้าใจกระบวนการเข้ารหัสโดยละเอียด ต่อไปนี้จะเข้ารหัส ISO-8859-1, GBK และ UTF-8 ผ่านรหัสง่าย ๆ
คลาสสาธารณะ test02 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่น unsupportencodexception {string string = "i am cm"; test02.printchart (string.tochararray ()); test02.printChart (string.getBytes ("ISO-8859-1")); test02.printChart (string.getBytes ("GBK")); test02.printChart (string.getBytes ("UTF-8")); } / *** แปลงถ่านเป็น hexadecimal* / โมฆะคงที่สาธารณะ printChart (char [] chars) {สำหรับ (int i = 0; i <chars.length; i ++) {system.out.print (integer.tohexstring (chars [i])+"); } system.out.println (""); } / ** * ไบต์แปลงเป็น hex * / โมฆะคงที่สาธารณะ printChart (byte [] bytes) {สำหรับ (int i = 0; i <bytes.length; i ++) {string hex = integer.tohexstring (ไบต์ [i] & 0xff); if (hex.length () == 1) {hex = '0' + hex; } system.out.print (hex.touppercase () + ""); } system.out.println (""); -เอาท์พุท:
6211 662F 20 63 6d 3F 3F 20 63 6D CE D2 CA C7 20 63 6D E6 88 91 E6 98 AF 20 63 6D
ผ่านโปรแกรมเราจะเห็นได้ว่าผลลัพธ์ของ "ฉันเป็น CM" คือ:
ถ่าน []: 6211 662F 20 63 6D ISO-8859-1: 3F 3F 20 63 6D GBK: CE D2 CA C7 20 63 6D UTF-8: E6 88 91 E6 98 AF 20 63 6D 6D
ภาพมีดังนี้: