ปัญหาการเข้ารหัส Java FileReader


130

ฉันพยายามใช้ java.io.FileReader เพื่ออ่านไฟล์ข้อความและแปลงเป็นสตริง แต่ฉันพบว่าผลลัพธ์นั้นเข้ารหัสผิดและไม่สามารถอ่านได้เลย

นี่คือสภาพแวดล้อมของฉัน:

  • Windows 2003, การเข้ารหัสระบบปฏิบัติการ: CP1252

  • Java 5.0

ไฟล์ของฉันเข้ารหัส UTF-8 หรือเข้ารหัส CP1252 และบางไฟล์ (ไฟล์ที่เข้ารหัส UTF-8) อาจมีอักขระจีน (ไม่ใช่ละติน)

ฉันใช้รหัสต่อไปนี้เพื่อทำงานของฉัน:

   private static String readFileAsString(String filePath)
    throws java.io.IOException{
        StringBuffer fileData = new StringBuffer(1000);
        FileReader reader = new FileReader(filePath);
        //System.out.println(reader.getEncoding());
        BufferedReader reader = new BufferedReader(reader);
        char[] buf = new char[1024];
        int numRead=0;
        while((numRead=reader.read(buf)) != -1){
            String readData = String.valueOf(buf, 0, numRead);
            fileData.append(readData);
            buf = new char[1024];
        }
        reader.close();
        return fileData.toString();
    }

โค้ดด้านบนใช้ไม่ได้ ฉันพบว่าการเข้ารหัสของ FileReader คือ CP1252 แม้ว่าข้อความจะเข้ารหัส UTF-8 แต่ JavaDoc ของ java.io.FileReader บอกว่า:

ตัวสร้างของคลาสนี้ถือว่าการเข้ารหัสอักขระดีฟอลต์และขนาดไบต์บัฟเฟอร์เริ่มต้นนั้นเหมาะสม

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


คุณควรคลาย String.valueOf () ภายในลูปและใช้ StringBuffer.append (char [], int, int) โดยตรง ซึ่งจะช่วยประหยัดการคัดลอกถ่าน [] ได้มาก แทนที่ StringBuffer ด้วย StringBuilder คำถามนี้ไม่เกี่ยวกับคำถามของคุณ '
Joachim Sauer

1
ฉันเกลียดที่จะพูด แต่คุณอ่าน JavaDoc ทันทีหลังจากส่วนที่คุณวางหรือไม่? คุณรู้หรือไม่ว่าส่วนที่ระบุว่า "หากต้องการระบุค่าเหล่านี้ด้วยตัวคุณเองให้สร้าง InputStreamReader บน FileInputStream"?
Powerlord

ขอบคุณสำหรับความคิดเห็นของคุณจริงๆแล้วฉันอ่าน JavaDoc แต่สิ่งที่ฉันไม่แน่ใจคือฉันควรระบุค่าเหล่านี้ด้วยตัวเองหรือไม่และเปลี่ยนเป็น "สร้าง InputStreamReader บน FileInputStream"
nybon

ใช่ถ้าคุณรู้ว่าไฟล์อยู่ในสิ่งอื่นที่ไม่ใช่การเข้ารหัสเริ่มต้นของแพลตฟอร์มคุณต้องบอก InputStreamReader ว่าจะใช้ไฟล์ใด
Alan Moore

คำตอบ:


248

ใช่คุณต้องระบุการเข้ารหัสของไฟล์ที่คุณต้องการอ่าน

ใช่หมายความว่าคุณต้องรู้การเข้ารหัสของไฟล์ที่คุณต้องการอ่าน

ไม่ไม่มีวิธีทั่วไปในการคาดเดาการเข้ารหัสของไฟล์ "ข้อความธรรมดา" ที่กำหนด

ข้อโต้แย้งที่หนึ่งการก่อสร้างของFileReaderมักจะใช้การเข้ารหัสแพลตฟอร์มเริ่มต้นซึ่งโดยทั่วไปความคิดที่ดี

เนื่องจาก Java 11 FileReaderได้รับตัวสร้างที่ยอมรับการเข้ารหัส: new FileReader(file, charset)และnew FileReader(fileName, charset).

ใน java เวอร์ชันก่อนหน้าคุณจำเป็นต้องใช้ไฟล์.new InputStreamReader(new FileInputStream(pathToFile), <encoding>)


1
InputStream คือ = FileInputStream ใหม่ (ชื่อไฟล์); ที่นี่ฉันได้รับไฟล์ข้อผิดพลาดไม่พบข้อผิดพลาดกับชื่อไฟล์รัสเซีย
Bhanu Sharma

3
+1 สำหรับคำแนะนำในการใช้ InputStreamReader อย่างไรก็ตามการใช้ลิงก์ในบล็อกโค้ดทำให้ยากต่อการคัดลอกและวางโค้ดหากสามารถเปลี่ยนแปลงได้ขอบคุณ
Ferrybig

1
จะเป็น "UTF-8" หรือ "UTF8" ในการเข้ารหัส ตามการอ้างอิงของ Java SE เกี่ยวกับการเข้ารหัสเนื่องจากInputStreamReaderเป็นjava.ioคลาสจึงเป็น "UTF8"?
NobleUplift

9
@NobleUplift: การเดิมพันที่ปลอดภัยที่สุดคือStandardCharsets.UTF_8ไม่มีโอกาสที่จะพิมพ์ผิดที่นั่น ;-) แต่ใช่ถ้าคุณใช้สตริง"UTF8"จะถูกต้อง (แม้ว่าฉันจะจำได้ว่าจะยอมรับทั้งสองวิธี)
Joachim Sauer

1
@JoachimSauer อันที่จริงนี่เป็นหนึ่งในจุดประสงค์ของByte Order Markพร้อมกับ .. ดี .. สร้างคำสั่งไบต์! :) ด้วยเหตุนี้ฉันจึงพบว่ามันแปลกที่ FileReader ของ Java ไม่สามารถตรวจจับ UTF-16 ที่มี BOM แบบนั้นได้โดยอัตโนมัติ ... ในความเป็นจริงฉันเคยเขียนUnicodeFileReaderว่าทำอย่างนั้น น่าเสียดายที่เป็นแหล่งปิด แต่ Google มีUnicodeReaderซึ่งคล้ายกันมาก
Stijn de Witt

79

FileReader ใช้การเข้ารหัสเริ่มต้นของแพลตฟอร์มของ Java ซึ่งขึ้นอยู่กับการตั้งค่าระบบของคอมพิวเตอร์ที่ทำงานอยู่และโดยทั่วไปแล้วเป็นการเข้ารหัสที่ได้รับความนิยมมากที่สุดในหมู่ผู้ใช้ในภาษานั้น ๆ

หาก "การเดาที่ดีที่สุด" นี้ไม่ถูกต้องคุณจะต้องระบุการเข้ารหัสอย่างชัดเจน ขออภัยFileReaderไม่อนุญาตสิ่งนี้ (การกำกับดูแลหลักใน API) แต่คุณต้องใช้new InputStreamReader(new FileInputStream(filePath), encoding)และรับการเข้ารหัสจากข้อมูลเมตาเกี่ยวกับไฟล์


24
"การกำกับดูแลที่สำคัญใน API" - ขอบคุณสำหรับคำอธิบายนี้ - ฉันสงสัยว่าทำไมฉันไม่พบตัวสร้างที่ฉันตามมา! Cheers John
monojohnny

@Bhanu Sharma: นั่นเป็นปัญหาการเข้ารหัสในระดับอื่นตรวจสอบว่าคุณได้ชื่อไฟล์มาจากที่ใดและมีการเข้ารหัสแบบฮาร์ดว่าการเข้ารหัสใดที่คอมไพเลอร์ใช้
Michael Borgwardt

1
@BhanuSharma: ปัญหาการเข้ารหัสชื่อไฟล์ไม่เกี่ยวข้องกับคำถามนี้ ดูหนึ่งในคำถาม“ ทำไมชื่อไฟล์ Unicode ไม่ทำงานใน Java” ที่มีอยู่มากมาย สปอยเลอร์: java.io API เช่น FileReader ใช้การเรียกระบบไฟล์ไลบรารีมาตรฐาน C ซึ่งไม่รองรับ Unicode บน Windows ลองใช้ java.nio แทน
bobince

1
" FileReaderใช้การเข้ารหัสเริ่มต้นแพลตฟอร์มของ Java ซึ่งขึ้นอยู่กับการตั้งค่าระบบของคอมพิวเตอร์ที่ทำงานอยู่และโดยทั่วไปเป็นการเข้ารหัสที่ได้รับความนิยมมากที่สุดในหมู่ผู้ใช้ในภาษานั้น" ฉันคงไม่พูดแบบนั้น อย่างน้อยของ Windows ด้วยเหตุผลทางเทคนิค / ประวัติศาสตร์บางแปลก JVM ที่ไม่สนใจความจริงที่เป็น Unicode แนะนำการเข้ารหัสบน Windows สำหรับ 'การใช้งานใหม่ทั้งหมดและแทนที่จะเสมอทำหน้าที่เป็นมรดกถ้าการเข้ารหัสการกำหนดค่าเป็นทางเลือกสำหรับแอปแบบเดิมคือ 'เริ่มต้นแพลตฟอร์ม'
Stijn de Witt

6
ฉันจะพูดไปไกลถึงว่าหากแอป Java ของคุณไม่ระบุการเข้ารหัสอย่างชัดเจนทุกครั้งที่อ่านหรือเขียนไปยังไฟล์ / สตรีม / ทรัพยากรมันจะเสียเพราะมันไม่สามารถทำงานได้อย่างน่าเชื่อถือ
Stijn de Witt


6

สำหรับ Java 7+ docคุณสามารถใช้สิ่งนี้:

BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);

นี่คือเอกสาร Charsets ทั้งหมด

ตัวอย่างเช่นหากไฟล์ของคุณอยู่ใน CP1252 ให้ใช้วิธีนี้

Charset.forName("windows-1252");

นี่คือชื่อบัญญัติอื่น ๆ สำหรับการเข้ารหัส Java ทั้งสำหรับเอกสาร IO และ NIO

หากคุณไม่ทราบว่ามีการเข้ารหัสที่คุณมีในไฟล์ที่คุณอาจใช้ libs ของบุคคลที่สามบางอย่างเช่นเครื่องมือนี้จาก Google นี้ซึ่งทำงานค่อนข้างเรียบร้อย


1

FileInputStream กับ InputStreamReader ดีกว่าการใช้ FileReader โดยตรงเนื่องจากไม่อนุญาตให้คุณระบุชุดการเข้ารหัส

นี่คือตัวอย่างการใช้ BufferedReader, FileInputStream และ InputStreamReader ร่วมกันเพื่อให้คุณสามารถอ่านบรรทัดจากไฟล์ได้

List<String> words = new ArrayList<>();
List<String> meanings = new ArrayList<>();
public void readAll( ) throws IOException{
    String fileName = "College_Grade4.txt";
    String charset = "UTF-8";
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(
            new FileInputStream(fileName), charset)); 

    String line; 
    while ((line = reader.readLine()) != null) { 
        line = line.trim();
        if( line.length() == 0 ) continue;
        int idx = line.indexOf("\t");
        words.add( line.substring(0, idx ));
        meanings.add( line.substring(idx+1));
    } 
    reader.close();
}

0

สำหรับภาษาอื่นเป็นภาษาละตินเช่นซิริลลิกคุณสามารถใช้สิ่งนี้:

FileReader fr = new FileReader("src/text.txt", StandardCharsets.UTF_8);

และตรวจสอบให้แน่ใจว่า.txtไฟล์ของคุณถูกบันทึกด้วยรูปแบบUTF-8(แต่ไม่ใช่ค่าเริ่มต้นANSI) ไชโย!

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