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

วิธีอ่านไฟล์ในรูปแบบ 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 สิ้นสุดลง เปิดตัวอ่านใหม่สำหรับทุกรอบที่อ่านไฟล์
