Java BufferedReader: วิธีอ่านไฟล์พร้อมตัวอย่าง

⚡ สรุปอย่างชาญฉลาด

Java BufferedReader อ่านข้อความจากสตรีมอินพุตโดยการบัฟเฟอร์อักขระ ซึ่งทำให้การอ่านไฟล์ทีละบรรทัดรวดเร็วและง่ายดาย Wrapping ตัวอ่านไฟล์ภายใน BufferedReader และ looping ฟังก์ชัน `on readLine until it returns null` จะพิมพ์ไฟล์ทั้งหมดออกมา

  • ???? วัตถุประสงค์หลัก: BufferedReader จะห่อหุ้ม Reader ใดๆ ก็ตามที่มีการดำเนินการอ่านข้อมูลที่ใช้ต้นทุนสูง โดยจะลดจำนวนการอ่านแต่ละครั้งจากสตรีมข้อมูลพื้นฐานลง
  • 🔁 ลูปการอ่าน: ฟังก์ชัน readLine จะส่งคืนหนึ่งบรรทัดต่อการเรียกใช้หนึ่งครั้ง และค่า null เมื่อสิ้นสุดไฟล์ ดังนั้นลูป while ที่ใช้กับผลลัพธ์ดังกล่าวจะวนไปทั่วทั้งไฟล์
  • 🧱 เส้นทางหลบหนี: A Windows เส้นทางภายใน Java สตริงต้องมีเครื่องหมายแบ็กสแลชสองตัวติดกัน มิเช่นนั้นคอมไพเลอร์จะปฏิเสธลำดับการหลีกเลี่ยงที่ไม่ถูกต้อง
  • ♻️ การเผยแพร่ทรัพยากร: การปิดตัวอ่านจะปลดปล่อยตัวจัดการไฟล์ที่เกี่ยวข้อง และ try-with-resources จะทำการปิดนั้นโดยอัตโนมัติ
  • ⌨️ อินพุตคอนโซล: ห่อping การใช้ System.in ใน InputStreamReader ช่วยให้คลาสเดียวกันสามารถอ่านข้อมูลจากแป้นพิมพ์ได้ แทนที่จะอ่านจากไฟล์
  • 🇧🇷 ความคมชัดของเครื่องสแกน: BufferedReader เร็วกว่าและส่งคืนบรรทัดดิบ ในขณะที่ Scanner แยกวิเคราะห์โทเค็นและประเภทข้อมูลพื้นฐานโดยตรง
  • 🔤 ข้อควรระวังเกี่ยวกับการตั้งค่าอักขระ: FileReader ใช้ชุดอักขระเริ่มต้น ดังนั้นการระบุชุดอักขระอย่างชัดเจนจะช่วยหลีกเลี่ยงอักขระผิดเพี้ยนในเครื่องต่างๆ

BufferedReader ใน Java

วิธีอ่านไฟล์ในรูปแบบ Java?

Java มีกลไกหลายอย่างในการอ่านข้อมูลจากไฟล์ แพ็กเกจที่มีประโยชน์ที่สุดสำหรับงานนี้คือ java.ioซึ่งมีกล้ามท้องtract Reader คลาสนี้กำหนดการอ่านสตรีมอักขระ java.io.BufferedReader เป็นคลาสย่อยที่เป็นรูปธรรมซึ่งใช้สำหรับการอ่านแบบบรรทัดต่อบรรทัดอย่างมีประสิทธิภาพ

ความหมายของ BufferedReader ใน Java?

BufferedReader คือ Java คลาสที่อ่านข้อความจากสตรีมอินพุต เช่น ไฟล์ โดยการบัฟเฟอร์อักขระเพื่อให้สามารถอ่านอักขระ อาร์เรย์ และบรรทัดได้อย่างมีประสิทธิภาพ หากไม่มีการบัฟเฟอร์ การร้องขอการอ่านแต่ละครั้งที่ส่งไปยัง Reader จะกระตุ้นการร้องขอการอ่านที่สอดคล้องกันไปยังสตรีมอักขระหรือไบต์ที่อยู่เบื้องหลัง

ดังนั้นจึงแนะนำให้ห่อ BufferedReader รอบๆ Reader ใดๆ ก็ตามที่มี read() การดำเนินการบางอย่างอาจมีค่าใช้จ่ายสูง เช่น FileReader และ InputStreamReaderโดยทั่วไปแล้ว การส่งพาธของไฟล์ไปยังตัวอ่านจะทำได้ดังนี้:

// Note the doubled backslash: a single \ starts an escape sequence
objReader = new BufferedReader(new FileReader("D:\\DukesDiary.txt"));

นี่เป็นการโหลดไฟล์เข้าไป objReaderจากนั้นให้วนลูปผ่านเนื้อหาของไฟล์และพิมพ์แต่ละบรรทัด ลูป while ในโค้ดด้านล่างจะอ่านไฟล์จนกว่าจะถึงจุดสิ้นสุดของไฟล์

while ((strCurrentLine = objReader.readLine()) != null) {
    System.out.println(strCurrentLine);
}

strCurrentLine รักษาระดับปัจจุบันไว้ และ objReader.readLine() ฟังก์ชันนี้จะส่งคืนค่าสตริง ดังนั้นลูปจะทำงานซ้ำไปเรื่อยๆ ตราบใดที่ค่าที่ส่งคืนไม่ใช่ค่าว่าง และจะหยุดเมื่อพบค่าว่างเป็นครั้งแรก ซึ่งหมายถึงสิ้นสุดไฟล์

Bufferตัวอย่าง edReader

โค้ดด้านล่างนี้เป็นโค้ดที่สมบูรณ์ Java Bufferตัวอย่างการใช้งาน edReader:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFileExample {

    public static void main(String[] args) {
        BufferedReader objReader = null;
        try {
            String strCurrentLine;

            objReader = new BufferedReader(new FileReader("D:\\DukesDiary.txt"));

            while ((strCurrentLine = objReader.readLine()) != null) {
                System.out.println(strCurrentLine);
            }

        } catch (IOException e) {
            e.printStackTrace();

        } finally {
            try {
                if (objReader != null)
                    objReader.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

หมายเหตุ การขอ finally บล็อกเป็นเรื่องสำคัญ มันรับประกันว่า objReader.close() ฟังก์ชันนี้จะทำงานไม่ว่าจะมีข้อผิดพลาดเกิดขึ้นหรือไม่ ซึ่งจะปล่อยตัวจัดการไฟล์พื้นฐานและคงสถานะเดิมไว้ การจัดการหน่วยความจำ คาดเดาได้ การลองแบบซ้อนกันภายใน finally จำเป็นเพราะ close() ตัวมันเองประกาศ IOException.

Bufferตัวอย่าง edReader JDK7

ตั้งแต่ JDK 7 เป็นต้นไป try-with-resources จะลบออก finally บล็อกทั้งหมด ทรัพยากรใดๆ ที่ประกาศไว้ในวงเล็บของ try คำสั่งจะปิดโดยอัตโนมัติเมื่อบล็อกสิ้นสุดลง โดยจะเรียงลำดับย้อนกลับจากลำดับการสร้าง แม้ว่าจะมีข้อยกเว้นเกิดขึ้นก็ตาม:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFileExample_jdk7 {

    private static final String FILENAME = "D:\\DukesDiary.txt";

    public static void main(String[] args) {

        try (BufferedReader br = new BufferedReader(new FileReader(FILENAME))) {

            String strCurrentLine;

            while ((strCurrentLine = br.readLine()) != null) {
                System.out.println(strCurrentLine);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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

BufferedReader กับ Scanner: ควรใช้อันไหนดีกว่ากัน

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

แง่มุม BufferedReader เครื่องสแกนเนอร์
คืนสินค้า บรรทัดทั้งหมดเป็นสตริง โทเค็นและส่วนประกอบพื้นฐานที่ถูกแยกวิเคราะห์
การแยกวิเคราะห์ในตัว ไม่ — คุณต้องแยกตัวหรือเปลี่ยนศาสนาด้วยตัวเอง ใช่ — nextInt, nextDouble, บรรทัดถัดไป
บัฟเฟอร์เริ่มต้น อักขระ 8192 อักขระ 1024
ความเร็วในการประมวลผลไฟล์ขนาดใหญ่ ได้เร็วขึ้น ช้าลงเนื่องจากการแยกคำด้วยนิพจน์ทั่วไป (regex tokening)
ความปลอดภัยของเกลียว Syncโครไนซ์ ไม่ได้ซิงโครไนซ์
เหมาะสำหรับ การอ่านไฟล์ขนาดใหญ่ทีละบรรทัด ช่องป้อนข้อมูลแบบโต้ตอบขนาดเล็กที่ต้องการป้อนค่า

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

วิธีการอ่านข้อมูลจากคอนโซลโดยใช้ BufferedReader

คลาสเดียวกันนี้ยังอ่านข้อมูลจากแป้นพิมพ์ ซึ่งมีประโยชน์สำหรับโปรแกรมบรรทัดคำสั่งขนาดเล็ก และสำหรับการป้อนข้อมูลในการแข่งขันเขียนโค้ด System.in เนื่องจากเป็นสตรีมไบต์ จึงไม่สามารถส่งต่อไปยังได้ BufferedReader โดยตรง InputStreamReader แปลงไบต์เป็นอักขระ และ Bufferจากนั้น edReader จะเพิ่มฟังก์ชันการอ่านบรรทัดอย่างมีประสิทธิภาพเข้าไปบนส่วนเชื่อมต่อดังกล่าว

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ReadConsoleExample {

    public static void main(String[] args) throws IOException {

        // Bridge the System.in byte stream to a character stream
        try (BufferedReader reader =
                new BufferedReader(new InputStreamReader(System.in))) {

            System.out.print("Enter your name: ");
            String name = reader.readLine();

            System.out.print("Enter your age: ");
            // readLine always returns text, so convert it explicitly
            int age = Integer.parseInt(reader.readLine().trim());

            System.out.println("Hello " + name + ", age " + age);
        }
    }
}

มีรายละเอียดสามอย่างที่ควรจดจำ ประการแรก readLine() ค่าที่ได้จะเป็นสตริงเสมอ ดังนั้นจึงต้องแปลงค่าตัวเลขที่ป้อนเข้ามาก่อน Integer.parseInt หรือคล้ายกัน และการป้อนค่าที่ไม่ใช่ตัวเลขจะทำให้เกิดข้อผิดพลาด NumberFormatException — ห่อหุ้มการแปลงหากอินพุตไม่น่าเชื่อถือ ประการที่สอง การเรียกใช้ .trim() ลบอักขระขึ้นบรรทัดใหม่ที่อยู่ท้ายสุดออก Windows คอนโซลจะเพิ่มข้อมูล ซึ่งเป็นสาเหตุที่พบบ่อยของความล้มเหลวในการวิเคราะห์ ประการที่สาม หลีกเลี่ยงการปิดตัวอ่านที่ห่อหุ้มไว้ System.in หากโปรแกรมยังคงต้องการข้อมูลจากคอนโซลในภายหลัง การปิดตัวห่อหุ้มจะปิดสตรีมพื้นฐานสำหรับกระบวนการนั้นอย่างถาวร

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

แม้ว่าโค้ดจะถูกต้องแล้ว แต่ก็ยังมีข้อผิดพลาดบางอย่างปรากฏซ้ำๆ

ร่วมกัน Bufferข้อผิดพลาดของ edReader และวิธีแก้ไข

ส่วนมาก Bufferข้อผิดพลาดของ edReader มักเกิดจากเส้นทางไฟล์ การจัดการทรัพยากร หรือการเข้ารหัสอักขระ มากกว่าจากลูปการอ่านเอง ข้อผิดพลาดแต่ละข้อด้านล่างจะระบุข้อยกเว้นและวิธีแก้ไข

  • ตัวละครหลบหนีอย่างผิดกฎหมาย: เส้นทางดังกล่าวใช้เครื่องหมายแบ็กสแลชตัวเดียว ดังเช่น "D:\DukesDiary.txt". Double ไปให้ "D:\\DukesDiary.txt"หรือใช้เครื่องหมายทับ ซึ่ง Java ยอมรับเมื่อ Windows.
  • ไม่พบไฟล์: เส้นทางนี้เป็นเส้นทางสัมพัทธ์กับไดเร็กทอรีการทำงาน ไม่ใช่ไฟล์ต้นฉบับ พิมพ์ new File(name).getAbsolutePath() เพื่อดูว่าอยู่ที่ไหน Java กำลังดูอยู่จริงๆ
  • เกิด NullPointerException ในการอ่านครั้งแรก: ตัวแปร reader ไม่ได้รับการกำหนดค่าเนื่องจาก constructor เกิดข้อผิดพลาด และข้อยกเว้นนั้นถูกซ่อนไว้ ตรวจสอบดู การจัดการข้อยกเว้น ก่อนลูป
  • อักขระที่บิดเบี้ยวหรือเครื่องหมายคำถาม: ไฟล์นั้นเป็น UTF-8 แต่ FileReader ใช้ชุดอักขระเริ่มต้นของแพลตฟอร์ม หากต้องการระบุชุดอักขระอย่างชัดเจน เช่น new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8).
  • ปิดการถ่ายทอดสดแล้ว: ตัวอ่านถูกปิดภายในลูปหรือถูกนำกลับมาใช้ใหม่หลังจาก try-with-resources สิ้นสุดลง เปิดตัวอ่านใหม่สำหรับทุกรอบที่อ่านไฟล์

คำถามที่พบบ่อย

ใช่แล้ว อาร์กิวเมนต์ตัวที่สองของคอนสตรัคเตอร์จะกำหนดค่าบัฟเฟอร์เป็นจำนวนอักขระ ตัวอย่างเช่น new BufferedReader(reader, 16384)ค่าเริ่มต้น 8192 เหมาะสำหรับไฟล์ส่วนใหญ่ การเพิ่มค่านี้จะมีประโยชน์เฉพาะกับไฟล์ที่มีการอ่านลำดับขนาดใหญ่มากเท่านั้น

สำหรับไฟล์ขนาดเล็ก ใช่แล้ว เพราะมันจะส่งคืนรายการบรรทัดในการเรียกเพียงครั้งเดียว สำหรับไฟล์ขนาดใหญ่ ควรเลือกวิธีอื่น Files.newBufferedReader or Files.linesเนื่องจากฟังก์ชัน readAllLines จะเก็บไฟล์ทั้งหมดไว้ในหน่วยความจำพร้อมกัน

ใช่. AI ผู้ช่วยจะเขียนโค้ดส่วนการล้างบล็อก finally ใหม่ให้เป็น try-with-resources และแนะนำโค้ดที่เทียบเท่ากับ NIO ตรวจสอบชุดอักขระและพฤติกรรมของข้อยกเว้นในภายหลัง เนื่องจากการแปลงที่สร้างขึ้นมักจะละทิ้งการเข้ารหัสที่ระบุไว้อย่างชัดเจน

AI อ่านสแต็ก tracและระบุว่าสาเหตุเกิดจากไดเร็กทอรีการทำงานที่ไม่ถูกต้อง สิทธิ์การเข้าถึงไม่เพียงพอ หรือการเข้ารหัสไม่ตรงกัน ช่วยลดระยะเวลาในการวินิจฉัย แต่การแก้ไขยังคงต้องได้รับการตรวจสอบกับไฟล์จริงอยู่ดี

ไม่ ฟังก์ชัน readLine จะตัดตัวจบบรรทัดออก ดังนั้นสตริงที่ส่งคืนจึงไม่มีตัวขึ้นบรรทัดใหม่ต่อท้าย บรรทัดว่างในไฟล์จึงส่งคืนสตริงว่างแทนที่จะเป็นค่า null

สรุปโพสต์นี้ด้วย: