บทท่ี 7 การตรวจสอบและดักจับ error (Exceptions) 196เร่ิมตน กบั JavaEnter a divider: 0Exception: / by zerojava.lang.ArithmeticException: / by zero at ThrowsWithTry.main(ThrowsWithTry.java:27)Enter a number: 12Enter a divider: 3Result = 4.0Enter a number: popException in thread \"main\" java.lang.NumberFormatException: For inputstring: \"pop\" atjava.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Integer.parseInt(Integer.java:426) at java.lang.Integer.parseInt(Integer.java:476) at ThrowsWithTry.main(ThrowsWithTry.java:20)จากผลลัพธที่ไดจ ะเหน็ วา โปรแกรมของเราจะฟอ งถามกี ารหารดว ยศูนย และหยดุ ถา ขอ มูลไมใช int (ในตัวอยา งนผี้ ใู ชใ สค ําวา \"pop\") โปรแกรมของเราจะทํางานไปเร่อื ย ๆ ถาผใู ชใ สขอ มลู ทีถ่ กู ตอ งตามที่โปรแกรมไดก ําหนดไว แตน ไี่ มใ ชการตรวจสอบทีด่ ีเทา ไรนกั ถา เราตองการที่จะบอกผใู ชว า ขอ มูลไมถกู ตอ ง และใหใ สข อ มลู ใหมเ ราก็ตอ งแกไข code ของเราใหรองรับการทาํ ใหม ซึง่ อาจทําไดด ังนี้//ThrowsWithTry1.javaimport java.io.*;import java.lang.Integer;class ThrowsWithTry1 { public static void main(String[] args) throws IOException { BufferedReader buffer; InputStreamReader isr; String input; int first = 0, divider = 0; while(true) { System.out.print(\"Enter a number: \"); isr = new InputStreamReader(System.in); buffer = new BufferedReader(isr); //get first number input = buffer.readLine(); first = readInt(input); //get second number only if first number is valid if(first != -1) { System.out.print(\"Enter a divider: \"); input = buffer.readLine(); divider = readInt(input); } //perform division if both numbers are valid if(first != -1 && divider != -1) { try { double result = first / divider; System.out.println(\"Result = \" + result); } catch(ArithmeticException e) { System.out.println(\"Exception: \" + e.getMessage()); ภาควิชาคอมพิวเตอรธรุ กจิ วทิ ยาลัยฟารอีสเทอรน
บทที่ 7 การตรวจสอบและดกั จับ error (Exceptions) 197เร่ิมตนกับ Java e.printStackTrace(); } } } } //method to convert an int from a string public static int readInt(String in) throws IOException { try { int num = Integer.parseInt(in); return num; } catch(NumberFormatException e) { System.out.println(\"Exception caught: \" + e.getMessage()); return -1; } }}เราไดเขยี น method readInt() สําหรบั การเปลี่ยน string ท่อี านจาก buffer ใหเปน int โดยเราไดใ ช tryและ catch ในการตรวจสอบและดักจับ error ที่อาจเกดิ ขึน้ จากการใสข อ มลู ผดิ พลาดทีอ่ าจเกดิ ข้ึนของผูใช และเรากาํ หนดใหส งคา -1 กลบั ออกไปเพือ่ ใชเ ปนตัวบอกให code ใน while loop รูวา ขอมูลไมถกู ตอ ง เพือ่ ทเี่ ราจะไดไ มต องอานขอมลู ตัวทสี่ อง ในสว นของ code ใน while loop เราไดท าํ การเปลี่ยนแปลงการอาน และ การประมวลผลใหเ ปน//get second number only if first number is validif(first != -1) { System.out.print(\"Enter a divider: \"); input = buffer.readLine(); divider = readInt(input);}//perform division if both numbers are validif(first != -1 && divider != -1) { try { double result = first / divider; System.out.println(\"Result = \" + result); } catch(ArithmeticException e) { System.out.println(\"Exception: \" + e.getMessage()); e.printStackTrace(); }}โปรแกรมของเราจะทําการประมวลผลถา ขอ มลู ทง้ั สองเปนขอมลู ท่ีถกู ตอง ดงั นนั้ เราจงึ ตองทําการตรวจสอบวา first และ divider มีคา ทถี่ ูกตองหรือไมดว ยการตรวจสอบกับ -1 ซึง่ ถา ตรวจสอบแลวไดขอมูลที่ถูกตอ ง เราก็จะทาํ การประมวลผลขอ มูลท้ังสอง ลองมาดผู ลลพั ธก ันEnter a number: 23Enter a divider: 3Result = 7.0Enter a number: pException caught: For input string: \"p\"Enter a number: 3Enter a divider: pException caught: For input string: \"p\"Enter a number: 32Enter a divider: 0 ภาควิชาคอมพิวเตอรธ ุรกิจ วทิ ยาลยั ฟารอ ีสเทอรน
บทท่ี 7 การตรวจสอบและดกั จบั error (Exceptions) 198เรมิ่ ตน กับ JavaException: / by zerojava.lang.ArithmeticException: / by zero at ThrowsWithTry1.main(ThrowsWithTry.java:34)Enter a number: -1Enter a number: Exception caught: nullEnter a number:จะเหน็ ไดวา โปรแกรมของเราทาํ งานไดอยา งทไ่ี ดต ั้งใจไว คอื ตรวจสอบขอ มลู ทง้ั สอง พรอ มทั้งทาํ การประมวลผลถา ขอ มลู ทั้งสองถกู ตอ ง ผอู า นควรสงั เกตดวยวา โปรแกรมตวั อยา งนยี้ งั ไมส มบรณู เลยทีเดยี วดงั จะเหน็ ไดจ ากการใสขอ มลู ที่เปน -1 ทาํ ไม? พรอ มกันนี้เรายงั ไมส ามารถออกจากโปรแกรมได ถาไมใ ชคําสั่ง <crtl> + cในการเขียนโปรแกรมทตี่ องรองรบั การทํางานทม่ี กี ารตรวจสอบเพ่ือใหเ กิดความถูกตองนั้น เราตองคาํ นึงถงึ ปจ จัยหลายดา น ท้ังนีก้ ข็ ้ึนอยูก บั ลักษณะของงานทเ่ี ราตอ งเขยี นโปรแกรมรองรบั โปรแกรมตวั อยา งของเราตองการแสดงใหเห็นถึงการตรวจสอบและดกั จับ error และแกไขในระดับหนึ่งเทาน้นัดังนน้ั ผูเขยี นจงึ ไมไดแสดงถงึ การแกไ ขท้ังหมด เพราะขอ กาํ หนดของงานยงั ไมชดั เจนการสรา ง exception ข้นึ มาใชเองJava ยอมใหเราสรา ง exception ขน้ึ มาใชกับโปรแกรมตา ง ๆ ท่ีเราคดิ วานาจะเพ่มิ ขอมูลบางอยา งใหกับผใู ช ถาหากมี error เกิดขึน้ เชน การหารดว ยศูนยท ีเ่ ราไดเ ขียนข้ึนมากอ นหนา นี้ เราอาจตองการบอกรายละเอยี ดใหม ากกวาเดมิ แกผ ใู ช เราจะใชโปรแกรมการหารดวยศนู ยที่มกี ารเปลยี่ นแปลงบางอยา ง เปนตวั อยา งตอ ไป//TestInputValidation.javaimport java.io.*;import java.lang.Integer;class TestInputValidation { public static void main(String[] args) throws Exception { BufferedReader buffer; InputStreamReader isr; String input; int first, divider; while(true) { //get first number first = readInt(); //get second number only if first number is valid divider = readInt(); //perform division if both numbers are valid try { //calling method divide() int result = divide(first,divider); System.out.println(\"Result = \" + result); } //catching user defined exception catch(ZeroDivideException e) { e.printStackTrace(System.err); } } } //method to get an int from the keyboard ภาควิชาคอมพิวเตอรธุรกจิ วทิ ยาลยั ฟารอ สี เทอรน
บทท่ี 7 การตรวจสอบและดักจบั error (Exceptions) 199เริ่มตนกบั Java public static int readInt() throws Exception { BufferedReader buffer; InputStreamReader isr; String input = null; int result = 0; boolean ok = false; isr = new InputStreamReader(System.in); buffer = new BufferedReader(isr); //keep looping until user enter an int while(!ok) { System.out.print(\"Enter a number: \"); try { input = buffer.readLine(); result = Integer.parseInt(input); ok = true; } catch(NumberFormatException e) { System.out.println(e.getMessage()); e.printStackTrace(System.err); } } return result; } //method to divide first number by the second number public static int divide(int first, int second) throws ZeroDivideException { int result; try { result = first / second; } catch(ArithmeticException e) { throw new ZeroDivideException(); //throw new exception } return result; }}เราไดเปลี่ยนโปรแกรมกอนหนา นม้ี ากพอสมควรในการทีจ่ ะอานขอมูลใหถกู ตอง พรอมทง้ั แสดงถงึ การเขียน exception ขนึ้ มาใชเอง เราเริ่มดวยการสราง method readInt() ขึน้ มาใหม โดยกําหนดใหผ ูใชตอ งใสขอ มลู ใหถ กู ตอ ง ถา ไมถูกเราก็จะวนกลบั ไปถามใหมจ นกวา จะไดMethod ตัวทส่ี องทีเ่ ราสรางขนึ้ มาคือ method divide() ทีร่ บั parameter 2 ตัว ทําหนาทใ่ี นการหารตัวเลขตัวแรกดว ยตัวเลขตัวทสี่ อง ภายใน method divide() นี้เราจะดกั จบั error ที่เกิดขนึ้ ผานทางArithmeticException ดว ย การ throw exception ท่ีเราไดส รา งข้นึ เอง ดวยคําสั่งcatch(ArithmeticException e) { throw new ZeroDivideException(); //throw new exception}แตก อนทีเ่ ราจะเรยี กใช exception ทีว่ านี้เราตอ งสรา ง code สําหรับ exception เสยี กอ น ซง่ึ เราไดออกแบบใหเ ปน การดักจับแบบงา ย ๆ ดวยการสราง class ZeroDivideException จาก class Exceptionเพ่อื ท่ีจะใหเราสามารถทจี่ ะไดร บั การถา ยทอดคุณสมบัติจาก class Exception เรากาํ หนดให code ของclass ZeroDivideException มีดงั นี้//ZeroDivideException.java ภาควิชาคอมพิวเตอรธ รุ กิจ วทิ ยาลยั ฟารอสี เทอรน
บทที่ 7 การตรวจสอบและดักจับ error (Exceptions) 200เริม่ ตนกับ Javaclass ZeroDivideException extends Exception { //default constructor ZeroDivideException() { super(\"divide by zero\"); //call super class' constructor } ZeroDivideException(String message) { super(message); }}เราไมไ ดทาํ อะไรมากไปกวาเรยี กใช constructor ของ class Exception ดว ยขอความที่เราตองการใหปรากฏหาก error ท่วี าน้เี กิดขน้ึ (divide by zero)ภายในตัว method main() เอง code สวนทสี่ ําคญั คือtry { //calling method divide() int result = divide(first,divider); System.out.println(\"Result = \" + result);}//catching user defined exceptioncatch(ZeroDivideException e) { e.printStackTrace(System.err);}เราเรยี ก method divide() ภายใน try และถาหากการหารท่ีเกดิ ขึน้ มี error มนั ก็จะถูกจบั ใน catch blockทีเ่ ราไดเ รยี กดว ย object ทีม่ าจาก class ZeroDivideException ทเ่ี ราไดส รา งขน้ึ หลังจากทีล่ อง run ดูเราไดผลลัพธด งั นี้Enter a number: 45Enter a number: 36Result = 1Enter a number: 0Enter a number: 3Result = 0Enter a number: popFor input string: \"pop\"java.lang.NumberFormatException: For input string: \"pop\" atjava.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Integer.parseInt(Integer.java:426) at java.lang.Integer.parseInt(Integer.java:476) at TestInputValidation.readInt(TestInputValidation.java:47) at TestInputValidation.main(TestInputValidation.java:16)Enter a number: 3Enter a number: 0ZeroDivideException: divide by zero at TestInputValidation.divide(TestInputValidation.java:64) at TestInputValidation.main(TestInputValidation.java:23)Enter a number: null (เราออกจากโปรแกรมดว ยการกด <crtl> + c ณ ตําแหนง นี้)java.lang.NumberFormatException: null at java.lang.Integer.parseInt(Integer.java:394) at java.lang.Integer.parseInt(Integer.java:476) ภาควิชาคอมพิวเตอรธ รุ กจิ วิทยาลยั ฟารอสี เทอรน
บทที่ 7 การตรวจสอบและดักจบั error (Exceptions) 201เรม่ิ ตนกับ Java at TestInputValidation.readInt(TestInputValidation.java:47) at TestInputValidation.main(TestInputValidation.java:16)Enter a number:ผลลัพธท ไ่ี ดกเ็ ปนไปตามทีเ่ ราไดคาดไว คอื error ถกู ดกั จับไวไ ดห มด ผูอานควรสังเกตดว ยวาเรายังใชloop ใน method main() ดังนั้นการออกจาก loop กต็ อ งใช <crtl> + c เหมอื นเดิม ทาํ ใหมกี ารดกั จับerror ท่เี กิดข้นึ ครัง้ สดุ ทา ยกอนออกจากโปรแกรมอกี สว นหน่งึ ทน่ี า สนใจในโปรแกรมของเรากค็ อื การสง ขอความบอกถึงรายละเอียดของ error ผา นทางSystem.err ซึ่งเปน อกี ชอ งทางหนึง่ ท่ี Java มใี หใชส าํ หรับการสงขอ มลู ทไ่ี มใชข อ มลู ปกติออกทางหนา จอ ถงึ แมว า ขอมูลจะออกไปท่ีเดยี วกันแตช องทางการสง ออก ไมเหมอื นกัน สาเหตทุ ี่เปน เชน น้กี ็เพอ่ื ใหม ีการแยกแยะ error ออกจากขอมูลหรือผลลัพธป กตขิ องโปรแกรมเรามาดอู กี สกั ตวั อยา งหนึง่//CheckRange.javaimport java.lang.Integer;class CheckRange { public static void main(String[] args) { try { //getting input from argument list int first = Integer.parseInt(args[0]); int second = Integer.parseInt(args[1]); int third = Integer.parseInt(args[2]); //check if the third input is between first and second checked(first, second, third); System.out.println(\"Number \" + third + \" is in the given range\"); } catch(OutOfRangeException e) { e.printStackTrace(System.err); } } //method to validate range public static void checked(int start, int stop, int num) throws OutOfRangeException { if(num < start) { String errMsg = new String(num + \" < \" + start); throw new OutOfRangeException(errMsg, start, stop); } if(num > stop) { String errMsg = new String(num + \" > \" + stop); throw new OutOfRangeException(errMsg, start, stop); } }}โปรแกรมตวั อยา งนีร้ ับขอ มูลผา นทาง argument list ท่ีเปน ตวั เลขสามตัว โดยจะทําการตรวจสอบวาตวั เลขตัวทสี่ ามอยูร ะหวางตวั แรกและตวั ทสี่ องหรือไม ถา ไมกจ็ ะ throw exceptionOutOfRangeException ที่ไดเ ขียนขึน้ ดังนี้//OutOfRangeException.java ภาควิชาคอมพิวเตอรธ รุ กิจ วทิ ยาลยั ฟารอีสเทอรน
บทท่ี 7 การตรวจสอบและดกั จับ error (Exceptions) 202เริม่ ตนกบั Javapublic class OutOfRangeException extends Exception { //default constructor OutOfRangeException() {} //constructor OutOfRangeException(String message, int p, int q) { super(message); }}จะเหน็ วา โดยสว นใหญแ ลว เราจะเรียกใช super class constructor ใหทํางานใหเ รา มากกวา ที่จะเขยี นcode ข้ึนมา ซงึ่ เปน ส่งิ ทที่ าํ ใหก ารออกแบบของเราไวขึน้ เราเพยี งแตเขยี นถึงสิ่งท่บี งบอกถงึ error ทีเกิดขน้ึ วาเปน อะไรเทานัน้ เอง จากการ run ผลลัพธท ี่เราไดค อื>java CheckRange 12 45 569OutOfRangeException: 569 > 45 at CheckRange.checked(CheckRange.java:31) at CheckRange.main(CheckRange.java:15);E:\bc221Book\source>java CheckRange 12 45 2OutOfRangeException: 2 < 12 at CheckRange.checked(CheckRange.java:27) at CheckRange.main(CheckRange.java:15)E:\bc221Book\source>java CheckRange 12 45 15Number 15 is in the given rangeสรปุในการเขยี นโปรแกรมที่ดนี นั้ ผเู ขยี นจะตอ งคาํ นงึ ถงึ error ที่อาจเกิดขน้ึ ในโปรแกรม ซง่ึ ไดแบง ออกเปนสองลกั ษณะใหญ คือ error ทโ่ี ปรแกรมเมอรไ มม ที างแกไ ขได คอื อยนู อกเหนือการควบคมุ ของผพู ฒั นาโปรแกรม เชน error ที่เกิดจากหนว ยความจาํ ไมพอเพยี ง สว น error อีกอันหน่ึงทผี่ เู ขียนโปรแกรมตอ งตรวจสอบกค็ ือ error ทเ่ี กดิ ตอน run โปรแกรม เชน error จากการหารดว ยศนู ย การเขา หาIndex ของ array ที่ไมมีอยูจ รงิ การปอนขอมลู ท่ีไมถกู กับชนดิ ทต่ี องการ อยา งนเี้ ปน ตนException ท่ี Java มใี หม อี ยมู ากพอสมควร และมากพอทจี่ ะเรียกใชไดเ กือบทกุ กรณีของการเขียนโปรแกรม แตถ า ผูเขยี นโปรแกรมไดพฒั นาโปรแกรมท่ีไมส ามารถใช exception ท่ี Java มใี หก ส็ ามารถท่ีจะออกแบบ exception ข้นึ มาใชเ องไดในการดัก error นัน้ ผเู ขียนอาจสรา ง exception ขึน้ มาใชใ นการดักจบั error เองได ทั้งนีก้ ็ข้นึ อยกู ับลกั ษณะของงานที่ผูเขยี นโปรแกรมกําลงั พฒั นาอยู โดยสรุปแลวสงิ่ ทตี่ อ งควรคํานงึ ในการตรวจจบั errorคือ9 Exception เปนตวั บง ชถี้ ึง error ทเี่ กดิ ในโปรแกรม9 Exception เปน object ท่เี กิดมาจาก class Throwable9 เราสามารถทจี่ ะเรยี กใช exception ทม่ี าจาก Java โดยตรง หรอื สรา งขึ้นมาใชเ อง9 ถา เราตองการทจี่ ะควบคมุ exception ใน method เราตอ งเขยี น code ใน try block และไมจ าํ เปนที่ จะตองมี try block เพียงหนง่ึ block เทาน้ัน อาจมีมากกวา หน่ึง block ได9 Code ทใ่ี ชใ นการควบคุม หรอื แกไ ข exception จะตอ งทําใน catch block ทต่ี ามหลงั try block ทันทแี ละในหนง่ึ try block เราสามารถที่จะมี catch block มากกวา หนึง่ ตัวได9 Finally เปน block ทายสดุ ทอ่ี ยูใ น try โดยทวั่ ไปจะใช finally block ในการเก็บกวาดการทาํ งานของ โปรแกรม เชน การปดไฟล ภาควิชาคอมพิวเตอรธ ุรกจิ วทิ ยาลยั ฟารอ สี เทอรน
บทที่ 7 การตรวจสอบและดักจบั error (Exceptions) 203เร่มิ ตนกบั Javaแบบฝก หดั1. จงเขียนโปรแกรมทรี่ บั ขอ มูลจาก keyboard เฉพาะท่ีเปน int เทา นน้ั ใหใ ช exception ในการดักจบั error ทผี่ ูใชอาจใสผ ดิ เชน ใสต ัวอักษร ใหแ สดงขอความฟองทันทที ผ่ี ูใชใสผ ิด2. จงปรบั ปรงุ โปรแกรมทเี่ ขยี นขนึ้ ในขอหนึ่ง โดยเพ่ิมการตรวจสอบท่ไี มร ับตัวเลขทต่ี ่าํ กวาศนู ย3. จงเขียนโปรแกรมทรี่ บั ขอ มูลจาก keyboard เปนตวั เลขสามตวั โดยกาํ หนดให ตวั แรกเปน จดุ เร่ิมตน ตวั ทสี่ องเปน จดุ จบ ของคาขององศา Celsius และตวั ทส่ี ามเปน ตัวกําหนดการเพ่ิมคา (step) ให โปรแกรมทาํ การคํานวณหา องศา Fahrenheit ตามขอมูลท่ีกาํ หนดไวในสองตวั แรก และใหท ําการ เพิ่มข้ึนตามคา ของขอมูลตัวทส่ี าม ดงั ตัวอยา งผลลัพธท ่ีแสดงใหด ูนี้>java Temperature 0 100 10CELSIUS FAHRENHEIT0 32.010 50.020 68.030 86.040 104.050 122.060 140.070 158.080 176.090 194.0100 212.0ใหท าํ การตรวจสอบ และดกั จบั error ทอ่ี าจเกิดขึ้นจากการใสขอ มูลทีไ่ มถ กู ตอ ง เชน คาของการเพิ่มเปน ตวั เลขทนี่ อ ยกวา ศูนย (negative number) คา เร่มิ ตน มากกวาคา สดุ ทา ย และจาํ นวนขอ มลู ท่ีนําเขามีจํานวนไมครบตามท่กี าํ หนด ตวั อยา งของ error ทีเ่ กิดข้ึน >java Temperature 0 100 -5 3rd arg < 0 NUMBER = -5 >java Temperature 40 20 5 Ordering Err FIRST = 40 SECOND = 20 >java Temperature 10 40 java.lang.ArrayIndexOutOfBoundsException: at Temperature.main(Temperature.java:41)4. จงเขียนโปรแกรมทหี่ าคา ของ mn โดยกําหนดใหท ั้ง m และ n มาจาก command line argument list ใหทําการตรวจสอบและดกั จับ error ทกุ ๆ กรณที ่ีอาจทาํ ใหโปรแกรมไมทาํ งาน5. จงปรับปรงุ class Account ในบททห่ี กใหมกี ารตรวจสอบและดกั จับ error ทอ่ี าจเกดิ ข้นึ ในการ ถอน เงิน ฝากเงิน และ คดิ ดอกเบย้ี ถา method ใด ๆ ทท่ี าํ หนาทด่ี ังกลาวไมม ี ใหเ ขียนข้นึ ใหม พรอ มทั้ง เขียนโปรแกรมทดสอบ6. จงปรบั ปรงุ โปรแกรมในขอ เจด็ แปด และ เกา ในบททหี่ า ใหมกี ารตรวจสอบและดกั จับ error พรอ ม ทัง้ เขยี นโปรแกรมทดสอบ ภาควิชาคอมพิวเตอรธรุ กจิ วิทยาลัยฟารอสี เทอรน
ในบทท่แี ปดนเ้ี ราจะพดู ถึงการอา น และเขยี นขอ มลู ผา นทาง stream หรอื ชอ งทางการสง ขอ มูล เราจะดถู งึวธิ ีการ กระบวนการตาง ๆ ทเ่ี กย่ี วของกับขอมูลนําเขา และการนําขอมูลออกหลงั จากจบบทน้ีแลว ผูอา นจะไดทราบถงึ o ความหมายของ stream o Class ตา ง ๆ ท่ี Java มีใหใ นการประมวลผลดว ย stream o การสรา ง directory o การสรา ง file การเปดและปด file การอา นและเขยี น file o ขอ แตกตางระหวาง text file และ binary fileในการทํางานทเี่ ก่ียวของกับไฟลใ น Java นั้นจะตองทาํ ผา นทาง stream ซึ่งเปนตวั แทนของอุปกรณท ่ีนาํ เขา (input) หรอื สง ออก (output) ขอ มูล เราสามารถเขียน และอานขอ มลู ผานทาง stream ดว ยวิธกี ารตาง ๆ ในรปู แบบตา ง ๆ กัน เชน อา นขอ มลู จาก keyboard ทีละหน่งึ ตัวอกั ษร หรอื ทลี ะ 10 byteโดยทว่ั ไป ขอมลู นําเขาจะมาจาก keyboard หนว ยความจําสาํ รอง (disk) หรอื อปุ กรณอ น่ื ๆ เชนscanner light-pen memory-card แตโดยสว นใหญแ ลวมักจะมาจาก keyboard และ disk ในบทนี้เราจะใชอ ปุ กรณทงั้ สองท่ีไดกลาวมา เปนชอ งทางหลกั ในการนาํ เขา ขอมลู และจะใชช อ งทางสง ออกเพยี งสองทางคือ จอ (monitor) และ เครอื่ งพมิ พ (printer)รูปแบบพืน้ ฐานของขอมลู ท่ี Java รจู กั มีอยสู องลกั ษณะคอื binary streams และ character streams ซง่ึมคี วามแตกตางกันในรปู แบบของการจดั เก็บ ถาเราเขียนขอมลู เขาสู stream โดยเขยี นทลี ะ byte หรือ ทีละหลาย ๆ byte เราเรียกขอ มลู เหลา น้วี า binary data การเขยี นจะไมม ีกระบวนการอื่นใดมายงุ เกี่ยว (เชนการเปลยี่ นขอมลู ใหอ ยใู นรปู แบบอืน่ กอ นการจดั เกบ็ ) ขอ มูลจะถูกสง เขา สู stream ตามทปี่ รากฏอยใู นmemory เชน ถา memory สว นนเ้ี ก็บขอ มูลท่เี ปน ตวั เลขอยู ตัวเลขเหลานจี้ ะถูกสง เขา สู streams ตามที่ปรากฏใน memoryในการเกบ็ ขอมลู ทีเ่ ปน character streams นน้ั โดยทวั่ ไปจะทํากบั ขอ มูลทีเ่ ปนตัวอกั ษร (text) เราจะใชcharacter streams ในการอา นไฟลทเี่ ปน text ตัวเลขทอ่ี ยูในไฟลจ ะถูกเปล่ียนใหอยใู นรปู แบบท่ี Javaไดก ําหนดขึ้น การอา นตวั เลขดว ยการใช character streams เปนกระบวนการทย่ี งุ ยากมาก เพราะเราตอ งรวู า ตวั อักษรที่เปน ตวั แทนของตวั เลขนั้นใชก ตี่ วั อกั ษรในการเก็บ เชน ถา เราสง ตวั เลข 17 ผา นทางcharacter stream ตัวเลขนี้จะถูกเปลี่ยนใหเ ปนคา ASCII ของ '1' และ '7' หรือ 0x310x37 (เลขฐาน 16)และสมมตวิ า เราเขียนเลข 727 อกี ตวั ใน stream ของเราก็จะมขี อมูลเปน 0x310x370x370x320x37 การอานตัวเลขทัง้ สองตัวออกมาจะทาํ ไดย ากมากเพราะเราไมรวู า จะตอ งอานตวั อักษรทลี ะก่ตี วั ถงึ จะไดข อ มลูท่ีถกู ตองเหมอื กับที่ไดเขยี นไวเราไดทราบแลว วา Java เกบ็ ขอ มลู ในรูปแบบของ Unicode ดังนัน้ ในการเขยี นขอ มูลเขาสู stream ในรปู แบบของ character ขอ มูลจะถูกเปลยี่ น (โดยอตั โนมตั )ิ ใหอยใู นรปู แบบของการเกบ็ (local code)ตามท่ีเคร่อื งนัน้ ใชอ ยู เชน ถา เคร่ืองของเราใชภ าษาไทยเปน ภาษาหลกั รปู แบบของการเก็บกจ็ ะเปน codeของภาษาไทย (Java เรยี กระบบนีว้ า localization) ในการอา นขอ มูลนัน้ Java จะเปลย่ี นขอ มูลทีถ่ ูกจัดเก็บในรปู แบบของ local code ใหเ ปน Unicode กอน โดยสรุปแลว ในการเขยี นและอานน้นั Java จะเปล่ียนขอมลู ใหเปน Unicode สว นการจดั เกบ็ น้นั จะจัดเกบ็ ในรปู แบบของภาษาท่ใี ชอ ยใู นเครอ่ื ง
บทที่ 8 Streams I/O 205เร่ิมตนกบั Javaเราจะมาดตู วั อยา งทเี่ กยี่ วของกับไฟล ในหลาย ๆ รูปแบบเพอ่ื ทาํ ใหเกิดความเขา ใจถงึ วธิ ีการ และกระบวนการตาง ๆ ทเี่ ก่ียวของกบั input และ output เราจะเร่ิมตนดว ยการตรวจสอบขอมูลตาง ๆ ที่เกี่ยวกบั ไฟล จากโปรแกรม FileInfo.java//FileInfo.javaimport java.io.*;class FileInfo { public static void main(String[] args) { //get path from command-line argument File path = new File(args[0]); //display some info. about this file if(path.exists()) { System.out.println(path + \" does exist.\"); System.out.println(\"Readable : \" + statusOk(path.canRead())); System.out.println(\"Writable : \" + statusOk(path.canWrite())); System.out.println(\"Directory? : \" + statusOk(path.isDirectory())); System.out.println(\"File? :\"+ statusOk(path.isFile())); System.out.println(\"Hidden? : \" + statusOk(path.isHidden())); } else { System.out.println(path + \" does not exist.\"); }} //return \"Yes\" or \"No\" public static String statusOk(boolean yes) { return yes ? \"Yes\" : \"No\"; }}โปรแกรมเรม่ิ ตน ดว ยการสรา ง object path จาก class File ดวยขอมลู ทีม่ าจาก command-lineargument ซึ่งอยูใ นรูปแบบe:\bc221Book\sourcee:\bc221Book\source\source\bc221Book\source\FileInfo.javaตัวอยางสองตวั แรกอยูในรปู แบบท่เี รยี กวา absolute path สวนสองตัวสดุ ทา ยอยูในรปู แบที่เรยี กวาrelative pathabsolute path เปนการกําหนดท่ีมาท่ไี ปดว ยตวั อกั ษรของ drive สว น relative path จะไมมีการกาํ หนดตวั อกั ษรของ drive แตการทาํ งานท้ังหมดจะอยูภายใน directory นนั้ ๆ ดังตวั อยา งผลลัพธข องการ runนี้>java FileInfo \bc221Book\bc221Book does exist.Readable : YesWritable : Yes ภาควิชาคอมพิวเตอรธ รุ กจิ วทิ ยาลัยฟารอ สี เทอรน
บทท่ี 8 Streams I/O 206เริม่ ตนกับ JavaDirectory? : YesFile? : NoHidden? : No>java FileInfo \bc221Book\source\FileInfo.java\bc221Book\source\FileInfo.java does exist.Readable : YesWritable : YesDirectory? : NoFile? : YesHidden? : No>java FileInfo \bc221Book\FileInfo.java\bc221Book\FileInfo.java does not exist.ผลลัพธข องการ run ท้ังสามคร้ังใช relative path เปนตัวกําหนดการคนหา directory และ file สวนผลลพั ธด านลา งนี้เปนการาคน หาจาก absolute pathE:\bc221Book\source>java FileInfo e:\bc221Book\source\FileInfo.javae:\bc221Book\source\FileInfo.java does exist.Readable : YesWritable : YesDirectory? : NoFile? : YesHidden? : Noเราเรยี กใช method ทม่ี ีอยูใ น class File ในการตรวจสอบขอมูลของ file วา เปน file ลกั ษณะไหน ขอ มลูเฉพาะคอื อะไร คาํ วา file ใน Java นน้ั เปนไดส องอยาง คือ file ทีเ่ กบ็ ขอมูลที่เราใช (หรอื ระบบใช) และfile ทเ่ี กบ็ file หรอื ทร่ี ยี กวา directory (หรอื folder)Java ยงั มี method ที่ใชใ นการตรวจสอบขอมูลของ file อกี หลายตัว เราจะลองใช method เหลา นใ้ี นโปรแกรมตัวอยา งตอไปน้ี//FileInfo1.javaimport java.io.*;import java.util.Date;class FileInfo1 { public static void main(String[] args) { //explain how to use if no argument is given if(args.length < 1) { System.out.println(\"Usage: FileInfo1 file-name\"); System.exit(1); } //display information about this file echoFileInfo(new File(args[0])); }private static void echoFileInfo(File file) { //display if file is a file or a directory if(file.isFile()) System.out.println(\"\n\" + file + \" is a file.\"); if(file.isDirectory()) { String []list = file.list(); System.out.println(\"\n\" + file + \" is a directory.\"); System.out.println(\"There are \" + list.length + \" items in this directory.\"); ภาควิชาคอมพิวเตอรธุรกจิ วทิ ยาลัยฟารอ สี เทอรน
บทท่ี 8 Streams I/O 207เร่ิมตนกับ Java } //lastModifeid() returns amount of milliseconds the file //was last modified, e.g. 1045375241775 since //January 1, 1970, 00:00:00 GMT. We have to create a date //object from this data. long lastModified = file.lastModified(); Date dateModified = new Date(lastModified); //display information about this file System.out.println( \"Absolute path: \" + file.getAbsolutePath() + \"\n Name: \" + file.getName() + \"\n Parent: \" + file.getParent() + \"\n Path: \" + file.getPath() + \"\n Length: \" + file.length() + \"\n Last modified: \" + dateModified); }}โปรแกรม FileInfo1.java เรยี กใช method 5 ตัวในการแสดงขอ มูลของไฟลท ก่ี ําหนดให (ผานทางcommand-line argument) ซ่งึ เปนการเรียกใชโดยตรง และไมมีความยงุ ยากในการเรยี กใช มี methodเพยี งตวั เดยี วเทา นั้นทีเ่ ราตองปรับปรงุ ผลลพั ธท ่ี method ตัวนสี้ งมาให method ทว่ี านีค้ อื lastModified()lastModified() จะสง คา ที่มีชนดิ เปน long ทเ่ี ปน คาของ วันและเวลาท่ไี ฟลไดร บั การเปลย่ี นแปลงครง้ัสดุ ทายในรปู แบบของ millisecond เชน 1045375241775 ซ่งึ เราไมสามารถบอกไดว าเปน วนั ทเ่ี ทา ไรเวลาอะไร เราตอ งใชคา นใ้ี นการสรา ง วันขึน้ ใหมผานทาง class Date ดังน้ีlong lastModified = file.lastModified();Date dateModified = new Date(lastModified);หลงั จากนน้ั เราก็แสดงผลออกทางหนา จอ ดงั ที่แสดงใหด ูเปน ตัวอยางดานลางน้ี>java FileInfo1 e:\bc221Book\sourcee:\bc221Book\source is a directory.There are 328 items in this directory.Absolute path: e:\bc221Book\source Name: source Parent: e:\bc221Book Path: e:\bc221Book\source Length: 0 Last modified: Wed Feb 26 07:47:48 GMT+07:00 2003>java FileInfo1 FileInfo1.javaFileInfo1.java is a file.Absolute path: E:\b221Book\source\FileInfo1.java Name: FileInfo1.java Parent: null Path: FileInfo1.java Length: 1236 Last modified: Tue Feb 25 14:06:13 GMT+07:00 2003>java FileInfo1Usage: FileInfo1 file-nameตวั อยางตอไปเปน โปรแกรมทแ่ี สดงรายละเอยี ดของ directory วา ประกอบไปดวยไฟล หรอื sub-directory อะไรบา ง ภาควิชาคอมพิวเตอรธ ุรกิจ วทิ ยาลยั ฟารอ ีสเทอรน
บทท่ี 8 Streams I/O 208เริม่ ตนกบั Java//DirListing.java//adopted from Bruce Eckel'simport java.io.*;import java.util.Arrays;//extract file name from path//must implements FilenameFilter and override method accept()//to get files with given information into a listclass DirFilter implements FilenameFilter { String fileName;//constructorDirFilter(String fileName) { this.fileName = fileName;} //this method is called by method list() from class File //to include a file if it contains a given info public boolean accept(File path, String name) { //get only file name String file = new File(name).getName(); //if in fact the file exists we will get the first index //of this file, which causes accept() to return true, //otherwise we will get -1 and returns false return file.indexOf(fileName) != -1; }}class DirListing { public static void main(String[] args) { //create file object with a default directory File path = new File(\".\"); String[] fileList; //list of files with given criteria//no argument provided, get all files on this directoryif(args.length == 0) fileList = path.list();//information provided, get rid of path and extract files//with given info.else fileList = path.list(new DirFilter(args[0])); Arrays.sort(fileList); //sort the list showDir(fileList); //display the list} //display files to screen public static void showDir(String[] list) { for(int i = 0; i < list.length; i++) System.out.println(list[i]); }}กอ นทีจ่ ะอธบิ ายถึงการทํางานของโปรแกรม เรามาดผู ลลัพธ (ตดั ทอนบางสว นออก) กันกอ น>java DirListing java ภาควิชาคอมพิวเตอรธุรกจิ วทิ ยาลัยฟารอสี เทอรน
บทที่ 8 Streams I/O 209เร่ิมตนกับ JavaAccess.javaAccount.javaAdd1To100.java … …AddNumbers2.javaThrowsWithTry.javaThrowsWithTry1.javaTriangle.javaTryAndCatchExample1.javaTryAndCatchExample2.javaTwo.javaUpperLower.javaUpperLower2.javaVariables.javaVowels.javaZeroDivideException.javap.java>java DirListing teBankRate.classBankRate.javaByteShort.classByteShort.javaCalculate.classCalculate.javaโปรแกรม DirListing.java จะแสดงรายการของขอมูลทอ่ี ยูใน directory นน้ั ๆ ตามขอกําหนดท่ีเราต้ังใหเชนถาเราตอ งการแสดงไฟลท ัง้ หมดท่ีมีอยูเราก็ใชค าํ ส่ัง DirListing โดยไมมกี ารกําหนดใด ๆ แตถาตอ งการแสดงผลดว ยขอ กําหนด เชน ตอ งการแสดงไฟลท กุ ตัวท่ีมคี ําวา \"te\" อยใู นไฟลเ ราก็ใชคาํ สั่งDirListing te เปนตนโปรแกรมของเราตอ งเรยี กใช interface FileNameFilter เพอ่ื ทาํ การ override method accept() ทที่ าํหนา ทใ่ี นการกรองขอมูลสาํ หรบั ให method list() ท่อี ยูใ น class File ใช โดยกําหนดให methodaccept() ตรวจสอบไฟลจ าก path เฉพาะไฟลทถ่ี ูกตองตามเงอื่ นไขทีก่ ําหนดไวแ ลว จึงบอก methodlist() วา ไฟลต วั น้คี วรเกบ็ ไวใน list ประโยคทแ่ี สดงผลโดยไมต อ งกําหนดเงื่อนไข ตองตรวจสอบกบัargs.length ถาคา น้ีเปน 0 เราจะเก็บไฟลท กุ ตัวไวใน array list ดวยการเรียกใชfileList = path.list();แตถ า มกี ารกําหนดเง่อื นไข เราจะสงเงื่อนไขนไี้ ปให method accept() เพอื่ ทาํ การคัดเลือกไฟลทถ่ี ูกตอ งตามเง่อื นไข ดว ยประโยคfileList = path.list(new DirFilter(args[0]));สมมตวิ า args[0] ของเรามคี า เปน \"te\" (ดงั ตวั อยา ง) \"te\" ก็จะถูกเก็บไวเ ปน ตวั ตรวจสอบวา ไฟลท ี่อยูในpath มีคําน้อี ยหู รือไม เชน ไฟลช ่ือ Calculate.javaMethod list() กาํ หนดให parameter ทส่ี งเขา ไปเปน object จาก class FileNameFilter ดงั น้ันเราสามารถทจ่ี ะเขยี น method accept() ในลกั ษณะใดก็ได ในที่นเี้ ราดึงเอาเฉพาะชอ่ื ไฟลท ไี่ มม ี pathรวมอยดู ว ย ดว ยการกําหนดดงั นี้String file = new File(name).getName(); ภาควิชาคอมพิวเตอรธุรกิจ วทิ ยาลัยฟารอีสเทอรน
บทที่ 8 Streams I/O 210เรมิ่ ตนกับ Javaหลงั จากนัน้ เราใช method indexOf() จาก class String ในการตรวจสอบวา \"te\" มีอยูใ นไฟลน้หี รือไมซึ่งถามคี าที่ไดจ าก indexOf() จะเปน ตาํ แหนง ท่ี \"te\" อยู แตถาไมม ไี ดคา -1 เรานําคา น้มี าเปรยี บเทยี บกบั -1 เพอ่ื บอกให list() รูวา ควรเกบ็ ไฟลต ัวน้หี รอื ไม (true = เก็บ false = ไมเ กบ็ )return file.indexOf(fileName) != -1;ไฟลท กุ ตวั ท่เี ราไดจาก method list() จะถกู เก็บไวใ นตัวแปร fileList เพอ่ื เอาไวใ ชใ นการแสดงผลตอ ไปแตกอนที่เราจะแสดงผล เราทาํ การเรยี งลําดบั ของไฟลใน fileList ดว ยการเรยี กใช method sort() ของclass Arraysโปรแกรมตวั นี้อาจดูเขา ใจยากสักหนอ ย แตก ไ็ มย ากเกินไปนัก ผูอานตองทาํ ความเขา ใจในเรื่องของการเรยี กใช method list() ท่มี าจาก class File รวมไปถึงการเขยี น code ตามขอกําหนดท่เี ราตอ งการในmethod accept() ส่งิ ทเ่ี ราตอ งคํานึงถงึ คือMethod list() ใน class File มอี ยสู องตวั คอื ตวั ทีไ่ มมี parameter และตัวท่ีมี parameter สําหรบัmethod ตัวทม่ี ี parameter นี้ parameter ตอ งเปน object จาก class FileNameFilter ดงั นน้ั เราตองสรา ง class ทีม่ าจาก class น้ีพรอ มทงั้ ทําการ override method accept() ใหทาํ งานตามท่ีเราตองการผอู านควรทดลอง run โปรแกรมตัวนด้ี วยเง่อื นไขตาง ๆ เพื่อใหเ กดิ ความเขาใจมากยงิ่ ขนึ้ และควรทดลองเขยี น code ให method accept() ใหมโ ดยกาํ หนดใหทาํ การตา ง ๆ ทตี่ า งออกไปจากทีเ่ ขียนในโปรแกรมตัวอยา งน้ีเราไดพดู ถึงการแสดงรายการของไฟลท ม่ี ีอยใู น directory การสรางเงื่อนไขใหก บั list() และ accept()พอสมควรแลว ตอ ไปเราจะพูดถงึ การอาน การเขียน ไฟล โดยเราจะเรมิ่ ตนท่ี Input streamsInput and Output streams (ชอ งทางการนาํ ขอ มูลเขา และ การนาํ ขอ มลู ออก)Java กาํ หนดใหมกี ารนําขอ มลู เขา สโู ปรแกรมไดห ลายทาง ซ่ึงเรากไ็ ดส มั ผสั ถึงวิธกี ารมาบา งพอสมควรในเรือ่ งของการนาํ ขอ มลู เขาผา นทางชอ งทางการนาํ เขา มาตรฐาน (standard I/O) ตอ ไปนี้เราจะมาดูถึงการนําขอ มลู ทมี่ าจากไฟลเ ขาสูโปรแกรมรูปแบบของขอมูลทถ่ี ูกจัดเกบ็ ในไฟลน ้ันถกู แบง ออกเปนสองลักษณะคือ1. ขอ มลู ที่อยูในรปู แบบของ character เชน code ของโปรแกรมทสี่ รา งขึน้ จาก Text editor พดู งา ย ๆ ก็คือ เราสามารถทจ่ี ะอานขอมลู เหลานไ้ี ด เราเรยี กไฟลแ บบน้วี า text file2. ขอมลู ทีถ่ กู จดั เกบ็ ในรูปแบบของ binary data เชน ขอ มูลทถ่ี กู เกบ็ ในรปู แบบของ MS Word เรา ไมสามรถทจ่ี ะอา นขอมลู เหลานไี้ ดตอ งอาศัยโปรแกรมในการชวยอา น เราเรยี กไฟลในรูปแบบนี้ วา binary fileเราจะเร่มิ ดวยการอานขอมูลจาก text file ท่ีเก็บ code ของโปรแกรมที่เราไดเ ขยี นข้นึ//ReadingTextFile.javaimport java.io.*;class ReadingTextFile {//don't want to deal with error, let Java takes care of itpublic static void main(String[] args) throws IOException {BufferedReader in;//input bufferFileReader file; //input fileString str; //string to store characters read from filefile = new FileReader(args[0]); //open an input filein = new BufferedReader(file); //store contents in bufferint lineNo = 1; ภาควิชาคอมพิวเตอรธรุ กิจ วิทยาลยั ฟารอ สี เทอรน
บทท่ี 8 Streams I/O 211เรมิ่ ตนกับ Java //keep reading from buffer until null is reached while((str = in.readLine()) != null) { System.out.println(lineNo + \" \" + str); lineNo++; } in.close(); //manually close the file }}เราเปดไฟลท ี่ตอ งการอา นดวย FileReader() พรอ มกับการเรยี กใช BufferedReader() เพ่ือใหการอานขอมลู ทาํ ไดร วดเรว็ ข้ึน เราอานขอมลู เขา มาเกบ็ ไวใ น str ทลี ะหนึง่ แถวจนกวาขอมูลหมดจาก bufferหลังจากนน้ั เรากส็ งขอ มูลทอ่ี า นไดอ อกไปยงั หนา จอพรอมทง้ั เลขที่บรรทดั เม่อื สง ขอ มลู หมดแลวเรากป็ ดไฟลด วยการใช close() ผลลพั ธทเี่ ราไดจ ากการสงไฟล ReadingTextFile.java ไปใหโ ปรแกรมนี้ คือ1 //ReadingTextFile.java23 import java.io.*;45 class ReadingTextFile {6 //don't want to deal with error, let Java takes care of it7 public static void main(String[] args) throws IOException {8 BufferedReader in; //input buffer9 FileReader file; //input file10 String str; //string to store characters readfrom file1112 file = new FileReader(args[0]); //open an input file13 in = new BufferedReader(file); //store contents inbuffer1415 int lineNo = 1;16 //keep reading from buffer until null is reached17 while((str = in.readLine()) != null) {18 System.out.println(lineNo + \" \" + str);19 lineNo++;20 }21 in.close(); //manually close the file22 }23 }เรามาดตู ัวอยา งที่อา นขอมูลดว ยการใช FileInputStream() และ read() ในการอานกันบา ง//ReadTextFile.javaimport java.io.*;class ReadTextFile { public static void main(String[] args) throws IOException { FileInputStream in; //file input stream int ch; //store each character read try { in = new FileInputStream(args[0]); while((ch = in.read()) != -1) { System.out.print((char)ch); } in.close(); } ภาควิชาคอมพิวเตอรธรุ กจิ วทิ ยาลยั ฟารอ ีสเทอรน
บทที่ 8 Streams I/O 212เร่ิมตนกบั Java //file not found catch(FileNotFoundException e) { System.err.println(\"Cannot find: \" + args[0]); System.exit(1); } //no argument specified catch(ArrayIndexOutOfBoundsException e) { System.err.println(\"Usage: ReadTextFile file-name\"); System.exit(1); } }}โปรแกรมตวั นใี้ ช FileInputStream() เปน ตัวเปด ไฟล และใช read() ในการอานขอมูลทีละ byte จากFileInputStream ทําการจดั เก็บขอมูลทอ่ี านไดไวใน ch พรอ มทัง้ สงขอมูลท่อี านไดน ีอ้ อกไปทางหนาจอจนกวา การอานจะสิ้นสุดลง โดยอานเจอ -1 หรือ EOF (End Of File)ในการสงขอ มลู ทอี่ า นไดออกไปยังหนา จอนน้ั เราตองทาํ การ cast ใหก บั ch กอนมิเชนน้นั แลวขอมลู ที่สงออกไปจะไมตรงกับที่อานเขามา โปรแกรมของเรายังไดท าํ การตรวจจบั error ทอ่ี าจเกดิ ขึน้ สองตวั คอืโปรแกรมหาไฟลไ มเ จอ และ ผใู ชไมก าํ หนดชอ่ื ไฟลท ใ่ี ชเ ปน ขอ มลู สําหรับการเปดอานหลังจากทดลอง run ดว ยไฟล ReadTextFile.java ผลลัพธท ไ่ี ดคือ//ReadTextFile.javaimport java.io.*;class ReadTextFile {public static void main(String[] args) throws IOException { FileInputStream in; //file input stream int ch; //store each character read try { in = new FileInputStream(args[0]); while((ch = in.read()) != -1) { System.out.print((char)ch); } in.close(); } //file not found catch(FileNotFoundException e) { System.err.println(\"Cannot find: \" + args[0]); System.exit(1); } //no argument specified catch(ArrayIndexOutOfBoundsException e) { System.err.println(\"Usage: ReadTextFile file-name\"); System.exit(1); } }}character streams โดยท่วั ไปเหมาะสมกับการอา น และเขยี นขอมลู ท่เี ปน text ดังนนั้ หากจะเอามาใชกบัขอมูลอื่น ๆ กจ็ ะสรา งความยงุ ยากในการอานและเขียนมากพอสมควร ดังนน้ั ในการอานและเขยี นขอมูลท่ีไมใช text เราจงึ ตอ งใชการอา นและเขียนขอ มูลทีอ่ ยใู นรูปแบบของ binary data แตก อ นที่จะพดู ถึงเร่อื งของ Binary file เราจะมาดูกันถงึ ความยุง ยากท่ีวา ในการทาํ งานกบั text file ภาควิชาคอมพิวเตอรธุรกิจ วิทยาลัยฟารอ ีสเทอรน
บทท่ี 8 Streams I/O 213เรมิ่ ตน กบั Javaโปรแกรม TextWithStreamTok.java อานขอ มูลที่ถกู เกบ็ ไวในรปู แบบของ text นาํ มาประมวลผล เสรจ็แลวจงึ สงขอ มลู กลับไปยังหนา จอ เพือ่ ใหผอู า นเขาใจงา ยขน้ึ เราจงึ จดั เกบ็ ขอ มูลอยใู น formatString double intเชนNotebook 12.00 30CoffeeMug 5.00 40PaperCutter 12.50 25และเราจะกาํ หนดใหม ีขอ มลู เพียง 3 ตัวเทา นั้น ลองมาดูการใช StreamTokenizer ท่ีเราไดพดู ถงึ ตอนที่เราอา นขอมลู นาํ เขา จาก keyboard กอนหนา น้ี วา จะนํามาใชกบั ขอ มลู ท่อี ยูในไฟลไดอ ยา งไร ในโปรแกรมตวั อยา งดานลา งนี้//TextWithStreamTok.java - Reading text data from a fileimport java.io.*;class TextWithStreamTok { public static void main(String[] args) throws IOException { BufferedReader in = null; //input buffer FileReader file = null; //input file StreamTokenizer stream = null; //tokens try { file = new FileReader(args[0]); //get a file fromargument-list in = new BufferedReader(file); //storage buffer //create tokens stream = new StreamTokenizer(in); stream.eolIsSignificant(true); //EOL counted but ignored } catch(FileNotFoundException e) { System.err.println(\"Cannot find file: \" + args[0]); System.exit(1); } //arrays to store data only 3 items as example e.g. //description prices unit //Coffee-Mug 12.5 30 String[] descs = new String[3]; double[] prices = new double[3]; int[] units = new int[3]; int i = 0, j = 0, k = 0; //arrays' indices boolean first = true; //make sure correct data is read try { //reading tokens into corresponding arrays //until EOF is reached while(stream.nextToken() != StreamTokenizer.TT_EOF) { //data is a string if(stream.ttype == StreamTokenizer.TT_WORD) { descs[i++] = stream.sval; } //data is a number (price or unit) if(stream.ttype == StreamTokenizer.TT_NUMBER) { //first data in line is price ภาควิชาคอมพิวเตอรธุรกจิ วิทยาลัยฟารอ สี เทอรน
บทท่ี 8 Streams I/O 214เรมิ่ ตนกับ Java if(first == true) { prices[j++] = stream.nval; first = false; } //next one is unit price else { units[k++] = (int)stream.nval; first = true; } } //ignore EOL if(stream.ttype == StreamTokenizer.TT_EOL) { /* do nothing */ } } in.close(); //close the stream } //trouble reading file catch(IOException e) { System.err.println(\"Error reading file.\"); e.printStackTrace(); System.exit(1); } //too many items for arrays catch(ArrayIndexOutOfBoundsException e) { System.err.println(\"Array index out of bound.\"); e.printStackTrace(); System.exit(1); } display(descs, prices, units); //display data } //display data to screen private static void display(String[] d, double[] p, int[] u) { double total = 0.00; for(int i = 0; i < d.length; i++) { total = p[i] * u[i]; System.out.print(d[i] + \"\t\" + p[i]); System.out.println(\"\t\" + u[i] + \"\t\" + total); } }}การทํางานหลกั ๆ ของโปรแกรมก็คือ การอา นขอ มลู ทถ่ี กู จดั เก็บใน format ทไ่ี ดก ลา วไว สง่ิ สาํ คัญทเ่ี ราตองคาํ นงึ ถงึ คอื ตําแหนงและชนดิ ของขอมลู ทีเ่ ราตอ งอา น กอนอืน่ เราตองกาํ หนดชองทางนําเขาขอมูลดังนี้file = new FileReader(args[0]); //get a file from argument-listin = new BufferedReader(file); //storage buffer//create tokensstream = new StreamTokenizer(in);stream.eolIsSignificant(true); //EOL counted but ignoredหลงั จากนนั้ เรากต็ รวจสอบ token ทเี่ ราอานจาก stream วาเปน ตัวเลขหรือวา เปน stringwhile(stream.nextToken() != StreamTokenizer.TT_EOF) { //data is a string ภาควิชาคอมพิวเตอรธ ุรกิจ วิทยาลัยฟารอ ีสเทอรน
บทที่ 8 Streams I/O 215เริ่มตนกบั Java if(stream.ttype == StreamTokenizer.TT_WORD) { descs[i++] = stream.sval; } //data is a number (price or unit) if(stream.ttype == StreamTokenizer.TT_NUMBER) { //first data in line is price if(first == true) { prices[j++] = stream.nval; first = false; } //next one is unit price else { units[k++] = (int)stream.nval; first = true; } } //ignore EOL if(stream.ttype == StreamTokenizer.TT_EOL) { /* do nothing */ }}เราจะเกบ็ ขอมลู ท่เี ปน string ไวใ น descs[] ขอมลู ทีเ่ ปน double ไวใ น prices[] และขอมลู ท่เี ปน int ไวใน units[] สิ่งท่ีเราตองคํานึงก็คือ ลาํ ดบั ของขอมูลท่เี ปนตวั เลข เรารูว าตัวเลขกลุมแรกทอี่ านไดค อืprices และขอ มลู ถัดไปคือ units ดังนนั้ เราตอ งใชต วั แปร first เปนตัวกาํ หนดท่ีเกบ็ ขอ มลู วา ทีอ่ านไดค รงั้แรกควรไปอยทู ่ี prices[] และที่อา นไดถ ัดมาควรไปอยทู ี่ units[] และเมอ่ื เราอานจนหมดแลว เรากส็ งarrays ท้ังสามตวั ไปให display() ทาํ การประมวลผล และแสดงผลตอ ไปผลลพั ธท ี่เราได จากการ run คือ>java TextWithStreamTok data.datNotebook 12.0 30 360.0CoffeeMug 5.0 40 200.0PaperCutter 12.5 25 312.5ขอมูลทเ่ี กบ็ ไวในไฟล data.dat จะตอ งมชี อ งวางระหวางขอ มูลแตล ะตวั เชนNotebook 12.0 30CoffeeMug 5.0 40PaperCutter 12.5 25หรือจะใหอ ยใู นบรรทัดเดยี วกันก็ได เชนNotebook 12.0 30 CoffeeMug 5.0 40 PaperCutter 12.5 25ทง้ั น้ีเพราะ nextToken() จะอา นขอ มูลที่อยถู ดั จากชอ งวา งไปจนกวา จะเจอชอ งวา งอกี ครัง้ หน่ึงจะเหน็ ไดวา เราตอ งคอยระวังการอานขอมูล เพราะถา ลําดับของการอานขอมูลผดิ โปรแกรมของเราก็จะเกิดความผดิ พลาด และทส่ี ําคญั อีกอยางหนงึ่ คือ ขัน้ ตอนทยี่ งุ ยากของการกําหนดลาํ ดบั ของขอ มลู การอา นขอมลู (ลองนกึ ถงึ การอาน ถาเรามีขอมูลท่เี ปนตวั เลขอยมู ากกวา สองตวั และมีขอมลู ท่ีเปน string อยูระหวา งขอมลู เหลา น)ี้ ดังนนั้ เราจึงไมนยิ มใช text file ในการเก็บขอมูลทเ่ี ปน primitive type เราควรใชtext file ในการทํางานกับขอ มูลท่ีเปน text เทาน้ันตัวอยางตอ ไปเปน การใช StreamTokenizer นับจาํ นวนของคําทมี่ ีอยูใ นไฟล เพอ่ื ท่จี ะแสดงใหเหน็ วาถาขอ มูลเปน text ทงั้ หมดการทาํ งานจะไมมีปญหาเทาไร//WordCount.java - Using StreamTokenizer to count word ภาควิชาคอมพิวเตอรธ รุ กิจ วทิ ยาลยั ฟารอ สี เทอรน
บทท่ี 8 Streams I/O 216เร่มิ ตน กบั Javaimport java.io.*;class WordCount {public static void main(String[] args) throws IOException {//exit if there's no file specifiedif(args.length < 1) { System.out.println(\"Usage: WordCount file-name.\"); System.exit(1);}FileReader inFile = null; //file streamStreamTokenizer stream = null; //tokens streamint count = 0; //number of words in the file try { inFile = new FileReader(args[0]); stream = new StreamTokenizer(inFile); count = wordCount(stream); System.out.println(count + \" words counted in \" + args[0]); } catch(FileNotFoundException e) { System.err.println(\"Cannot find: \" + args[0]); e.printStackTrace(); System.exit(1); } finally { //close the file if(inFile != null) { try { inFile.close(); } catch(IOException e) { /* do nothing */ } } }} //method to count word in the stream private static int wordCount(StreamTokenizer s) throws IOException { int count = 0; try { while(s.nextToken() != StreamTokenizer.TT_EOF) { //count only word if(s.ttype == StreamTokenizer.TT_WORD) count++; } } catch(IOException e) { System.err.println(\"Error reading stream: \" + s); e.printStackTrace(); System.exit(1); } return count; }} ภาควิชาคอมพิวเตอรธ รุ กจิ วทิ ยาลยั ฟารอีสเทอรน
บทที่ 8 Streams I/O 217เร่มิ ตนกับ Javaโปรแกรม WordCount.java ทาํ การนบั จํานวนของคาํ ท่ปี รากฏอยใู นไฟลทมี่ าจาก command-lineargument โดยเรยี กใช StreamTokenizer เปนตัวตรวจสอบวา ขอ มูลทม่ี ีอยูใ นไฟลเปน string หรอื ไมถา เปน กจ็ ะนับ โดยไมส นใจถงึ รูปแบบของ string น้นั ๆ เชน \"s13\" ก็นบั เปน string โปรแกรมWordCount.java ของเราตวั นใี้ ช object จาก FileReader เปนตวั กําหนดชอ งทางผา นไปยังStreamTokenizer โดยตรง ซง่ึ ตางจากตวั อยา งกอนหนานี้ ท่เี ราใช BufferedReader เปน ที่เกบ็ ขอมูลการใช FileReader โดยตรงจะทาํ ใหเ สยี เวลาในการประมวลผลมากถาไฟลม ขี นาดใหญ เพราะฉะนน้ั ถาผูอานตอ งการความเรว็ ในการประมวลผล กต็ อ งใช BufferedReader เปนตวั เก็บขอ มูลทตี่ อ งการประมวลผลเราไดท ดลอง run โปรแกรมดว ยไฟลข อง Windows Xp Æ vbe6.dll ซึ่งมขี นาดเทา กบั 2,498,560bytes ผลลัพธท ไ่ี ดกอ นและหลงั การใช BufferedReader คอื>java WordCount vbe6.dllStart: Fri Feb 28 10:08:52 GMT+07:00 2003279829 words counted in vbe6.dllStop: Fri Feb 28 10:08:54 GMT+07:00 2003>java WordCount vbe6.dllStart: Fri Feb 28 10:10:35 GMT+07:00 2003279829 words counted in vbe6.dllStop: Fri Feb 28 10:10:36 GMT+07:00 2003จะเหน็ วา การประมวลผลดวยการใช BufferedReader ใชเ วลานอ ยกวา การใช FileReader โดยตรง (ดีข้ึนประมาณ 1 วนิ าท)ี เราใชการวดั หาเวลาอยา งงา ย ๆ ดวยการใช Date เปน ตวั บอกถึงความแตกตางกอนและหลังการอานขอมลู ในไฟล โดยเปล่ียนแปลง code บางสว นดังนี้ … //buffer holding dataBufferedReader buf = null; …try { System.out.println(\"Start: \" + new Date()); inFile = new FileReader(args[0]); buf = new BufferedReader(inFile); stream = new StreamTokenizer(buf); … … …finally { //close the file if(inFile != null) { try { inFile.close(); } catch(IOException e) { /* do nothing */ } } //close stream if(buf != null) { try { buf.close(); System.out.println(\"Stop: \" + new Date()); } catch(IOException e) { /* do nothing */ } }…… ภาควิชาคอมพิวเตอรธ รุ กจิ วิทยาลยั ฟารอีสเทอรน
บทท่ี 8 Streams I/O 218เร่มิ ตนกับ Javaอีกสงิ่ หน่ึงทเี่ ราไดพดู ถงึ กอนหนาน้ี แตไมเคยไดใชเ ลยกค็ ือ finally เพือ่ แสดงใหด ูถงึ ประโยชนข องfinally เราจงึ ใช finally เปน ตัวปด ไฟลใ นโปรแกรมใหเ รา ผอู านควรใช finally เปนตัวปด ไฟลเ สมอ(โปรแกรมตวั อยา งในบทนหี้ ลาย ๆ ตวั อาจไมใช finally เปน ตวั ปด ไฟล แตน นั่ ไมไ ดหมายความวา ผอู า นจะใชไมไ ด)ถาผอู านตอ งการทจ่ี ะจบั เวลาการทาํ งานของโปรแกรม หรือสวนของโปรแกรม Java มี method อกี ตัวหนงึ่ ทผี่ อู านสามารถเรยี กใชไ ดน น่ั ก็คือ method currentTimeMillis() ดงั ตัวอยางการใชท แ่ี สดงไวดานลา งน้ี//ElapsedTime.javaclass ElapsedTime { public static void main(String[] args) { long startTime, stopTime; //get starting time startTime = System.currentTimeMillis(); //do some looping for(int i = 0; i < 100000; i++) for(int j = 0; j < 100000; j++) ; //get ending time stopTime = System.currentTimeMillis(); //calculate elapsed time double elapsedTime = (double)(stopTime - startTime) / 1000.0; System.out.print(\"Elapsed time: \"); System.out.println(elapsedTime + \" seconds.\"); }}ผลลัพธท ่ีไดจ ากการ run คือElapsed time: 85.093 seconds.โปรแกรม ElapsedTime.java เรียกใช currentTimeMillis() กอนและหลงั จากการใช loop หลังจากน้นั ก็หาเวลาทใี่ ชไ ปดวยการนําเอาเวลาเรมิ่ ตนไปลบออกจากเวลาที่ไดหลังจากการใช loopเราสามารถทจี่ ะทําใหการทาํ งานกบั ขอ มูลทีเ่ ปน text ไดงา ยขึ้นถา เรากําหนดใหม ี ตวั แบงขอ มลู(delimiter) ระหวา งขอ มูลแตล ะตัวทอี่ ยใู นไฟล ซึ่งตัวแบง ทวี่ าน้ีจะเปนตวั อกั ษรใด ๆ กไ็ ดท ี่ไมมสี ว นเกี่ยวขอ งกบั ขอมลู ท่ีอยใู นไฟล เชน เราอาจใชเ ครอ่ื งหมาย \"|\" เปนตวั แบง ขอ มูลในไฟลต วั อยางทไ่ี ดพ ดูถึงกอ นหนา น้ี ลองมาดูตวั อยา งการใชต วั แบง ที่วา นีก้ นั//TokenWithDelimiter.java - Using '|' as delimiter in a text fileimport java.io.*;import java.util.StringTokenizer;import java.lang.Integer;class TokenWithDelimiter { public static void main(String[] args) throws IOException { //data to write to file String[] items = {\"Notebook\", \"Coffee Mug\", \"Paper cutter\"}; double[] prices = {12.0D, 5.0D, 12.5D}; ภาควิชาคอมพิวเตอรธุรกจิ วทิ ยาลัยฟารอ สี เทอรน
บทที่ 8 Streams I/O 219เรมิ่ ตน กับ Javaint[] units = {30, 40, 25}; //check to see if a file is provided if(args.length < 1) { System.err.println(\"Usage: TokenWithDelimeter file-name.\"); System.exit(1); } writeToFile(args[0], items, prices, units); processData(args[0]); }//writing data in arrays to a given fileprivate static void writeToFile(String fileName, String[] items, double[] prices, int[] units) throws IOException { File dataFile = null; FileWriter fWriter = null; BufferedWriter buffer = null; PrintWriter out = null;try { dataFile = new File(fileName); fWriter = new FileWriter(dataFile); buffer = new BufferedWriter(fWriter); out = new PrintWriter(buffer); //printing data to file with '|' in between for(int i = 0; i < items.length; i++) { out.print(items[i]); out.print('|'); out.print(prices[i]); out.print('|'); out.println(units[i]); } } catch(IOException e) { System.err.println(\"Error writing to file\"); e.printStackTrace(); System.exit(1); } finally { if(out != null) out.close(); }}//reading data from a given file & display to screenprivate static void processData(String fileName) throws IOException {String item; //item readdouble price; //price readint unit; //unit readFile dataFile = null;FileReader fReader = null;BufferedReader buffer = null;String input = null;try { ภาควิชาคอมพิวเตอรธุรกิจ วิทยาลยั ฟารอสี เทอรน
บทที่ 8 Streams I/O 220เริ่มตนกบั Java dataFile = new File(fileName); fReader = new FileReader(dataFile); buffer = new BufferedReader(fReader); input = buffer.readLine(); //read first line of data //keep reading until there's no more lines to read while(input != null) { //get token from input-line - skip '|' StringTokenizer token = new StringTokenizer(input, \"|\"); item = token.nextToken(); price = Double.parseDouble(token.nextToken()); unit = Integer.parseInt(token.nextToken()); //display to screen System.out.print(item + \"\t\" + price + \"\t\" + unit); System.out.println(\"\t\" + price * unit); input = buffer.readLine(); } } catch(IOException e) { System.err.println(\"Error reading file\"); e.printStackTrace(); System.exit(1); } finally { if(buffer != null) buffer.close(); } }}เราใชเครื่องหมาย '|' ในการแบง ขอ มูลทีอ่ ยใู นไฟล พรอมทง้ั ใช class StringTokenizer เปน ตัวดึงขอ มลูที่อา นมาจากไฟล หลังจากทเ่ี ราเขยี นขอ มลู ทอ่ี ยใู น arrays ทงั้ สามตวั ไปเก็บไวใ นไฟลด ว ยการใช print()และ println() ผอู านควรสังเกตถึงการเขียนขอ มลู ในแตล ะครง้ั วา เราเขยี น '|' ตามหลังเสมอ ยกเวน การเขียนขอ มลู ตัวสดุ ทา ยdataFile = new File(fileName);fWriter = new FileWriter(dataFile);buffer = new BufferedWriter(fWriter);out = new PrintWriter(buffer);//printing data to file with '|' in betweenfor(int i = 0; i < items.length; i++) { out.print(items[i]); out.print('|'); out.print(prices[i]); out.print('|'); out.println(units[i]);}หลงั จากนัน้ เรากอ็ านขอมูลกลบั ออกมาดว ย readLine() เราทําการดงึ ขอ มลู แตล ะตัวทถี่ กู แบง ดว ย '|' ดวยการใช nextToken() แตก อ นทเี่ ราจะทาํ การดงึ ขอ มลู ทกุ ครงั้ ในแตล ะบรรทดั เราจะตอ งกําหนดเงือ่ นไขใหtoken ของเราเสยี กอน ดว ยการใชStringTokenizer token = new StringTokenizer(input, \"|\");เพอื่ ทจี่ ะบงั คับใหก ารดึงขอมลู น้นั เกดิ ขึน้ ระหวางเคร่ืองหมาย '|' ขอมลู ตัวแรกท่ีเราดึงออกมาเปน Stringเราจงึ ไมตองแปลงขอมูลตัวนี้ แตข อมูลอกี สองตวั ทเ่ี ราดึงออกเปน double และ int ทถ่ี กู เก็บใหเ ปน ภาควิชาคอมพิวเตอรธรุ กจิ วทิ ยาลัยฟารอีสเทอรน
บทท่ี 8 Streams I/O 221เร่ิมตนกับ JavaString ในการเขียน ดังน้นั เราจึงตองแปลงขอ มูลทั้งสองตัวน้ี หลงั จากทเ่ี ราไดข อมูลทงั้ สามตัวแลว เราก็แสดงผลออกไปยังหนา จอdataFile = new File(fileName);fReader = new FileReader(dataFile);buffer = new BufferedReader(fReader);input = buffer.readLine(); //read first line of data//keep reading until there's no more lines to readwhile(input != null) { //get token from input-line - skip '|' StringTokenizer token = new StringTokenizer(input, \"|\"); item = token.nextToken(); price = Double.parseDouble(token.nextToken()); unit = Integer.parseInt(token.nextToken()); //display to screen System.out.print(item + \"\t\" + price + \"\t\" + unit); System.out.println(\"\t\" + price * unit); input = buffer.readLine();}ในการเขยี นไฟลของเราครง้ั นเี้ ราใช PrintWriter เปนตวั ชวย ซง่ึ ภายใน class PrintWriter นีเ้ ราสามารถที่จะเรยี กใช print() และ println() ได ทําใหก ารเขียนขอ มูลสง ไปยงั ไฟลของเราเหมอื นกบั การเขยี นทเี่ ราไดทํามากอนหนา นต้ี อนทเ่ี ราสง ขอมูลไปยังหนา จอ แตเราจะตอ งใช PrintWriter รว มกับ FileWriterเพอ่ื ใหก ารเขยี นไปยงั ไฟลท าํ ได และ BufferedWriter เพอื่ ใหท าํ ไดอ ยา งมปี ระสิทธภิ าพ (การใช bufferจะชว ยใหการทาํ งานกับขอมลู ไวขึน้ )ผลลัพธท ไ่ี ดจ ากการ run>java TokenWithDelimiter tokens.datNotebook 12.0 30 360.0Coffee Mug 5.0 40 200.0Paper cutter 12.5 25 312.5Binary fileในการเขียน binary data เขา สูไฟลน ั้นเราจะตองคํานึงถงึ วธิ กี ารในการท่จี ะเขยี นขอมลู เพราะในการเขยี นขอมลู เขา สูไ ฟลนั้น มีผลตอ การอา นขอ มลู กลบั ออกมา ลําดับของขอ มลู ทเ่ี ขยี นเขา สูไ ฟล จะตอ งอา นกลบัออกมาดว ยขัน้ ตอนเดียวกนัในการเขียนขอ มูลท่ีเปน binary data นนั้ เราสามารถที่จะเลอื กใช method หลาย ๆ ตวั ที่ Java มีใหเปนตัวชวยในการเขยี น เชนMethod Throws ความหมายwriteBoolean(boolean) IOException เขยี นคาของ boolean จํานวน 1 bytewriteShort(int) IOException เขยี นคา ของ short จาํ นวน 2 bytewriteInt(int) IOException เขยี นคาของ int จาํ นวน 4 bytewriteLong(long) IOException เขียนคา ของ long จาํ นวน 8 bytewriteFloat(float) IOException เขยี นคา ของ float จาํ นวน 4 bytewriteDouble(double) IOException เขยี นคา ของ double จาํ นวน 8 bytewriteChar(int) IOException เขียนคาของ char จํานวน 2 bytewriteChars(String) IOException เขียน String จํานวน 2 byte ตอ 1 ตวั อักษรwriteUTF(String) IOException เขยี น String จํานวน 1 byte ตอ ตัวอกั ษร บวกอีก 2 byte สําหรบั คา ของความยาวของ Stringสาํ หรับการอา นน้ัน เราจะใช method น้ี ภาควิชาคอมพิวเตอรธ รุ กิจ วทิ ยาลยั ฟารอ สี เทอรน
บทที่ 8 Streams I/O 222เรม่ิ ตนกับ Java Throws ความหมาย Method EOFException อาน 1 byte สง คา boolean กลับ readBoolean() EOFException อา น 2 byte สง คา Short กลบั readShort() EOFException อา น 4 byte สง คา int กลบั readInt() EOFException อาน 8 byte สง คา long กลบั readLong() EOFException อาน 4 byte สง คา float กลับ readFloat(0 EOFException อาน 8 byte สง คา double กลบั readDouble() EOFException อา น 2 byte สง คา char กลับ readChar() EOFException อา น String UTF readUTF() EOFException ไมอา นขอ มูลจํานวนเทากบั byte ทีก่ ําหนด skipBytes(int)ตวั อยา งตอไปจะเปนตัวอยา งการเขียนขอมูลเขาสไู ฟลในรูปแบบของ binary data หลงั จากนัน้ จะอา นกลับออกมา เราจะเร่มิ ตนดว ยการสราง record จาํ นวน 3 ตัว สรางไฟลส ําหรบั เกบ็ record เหลาน้ี อานrecord ออกมาทลี ะตัว ประมวล พรอมทั้งแสดงผลไปยงั หนา จอRecord คอื กลมุ ขอ มลู ทปี่ ระกอบไปดวยขอ มลู ตาง ๆ ซึ่งอาจเปนขอมลู ชนดิ เดยี วกันหรอื ขอ มูลตางชนดิ กนัก็ได ใน Java เราสราง record ดว ยการใช class เปน ตวั สรา ง เราจะกําหนดให record ของเรามี ขอมลูอยู 3 field เหมอื นกบั ตัวอยางกอ นหนานี้ คือ1. description2. price3. unitเรามาดโู ปรแกรมตวั อยา งกนั ดกี วา//DataFile.java - Sequential Access fileimport java.io.*;class DataFile {public static void main(String[] args) throws IOException {FileOutputStream fout; //file output streamDataOutputStream dout; //data output streamFileInputStream fin; //file input streamDataInputStream din; //data input stream//setting up data to be written to a fileString[] desc = {\"Coffee Mug\", \"Bookmark\", \"Note book\"};double[] prices = {5.00, 2.00, 10.00};int[] unit = {12, 50, 30};//variables storing data read from fileString description;double price, total = 0.00;int unitPrice;//open file for writingtry { fout = new FileOutputStream(args[0]); dout = new DataOutputStream(fout); for(int i = 0; i < desc.length; i++) { dout.writeUTF(desc[i]); dout.writeDouble(prices[i]); dout.writeInt(unit[i]); } dout.close(); ภาควิชาคอมพิวเตอรธรุ กิจ วิทยาลยั ฟารอ ีสเทอรน
บทท่ี 8 Streams I/O 223เรม่ิ ตน กบั Java } catch(FileNotFoundException e) { System.err.println(\"Cannot open \" + args[0]); return; } catch(ArrayIndexOutOfBoundsException e) { System.err.println(\"Usage: DataFile file-name.\"); return; } //open file for input try { fin = new FileInputStream(args[0]); din = new DataInputStream(fin); System.out.println(\"Description\tPrice\tUnit\tTotal\"); while(true) { description = din.readUTF(); price = din.readDouble(); unitPrice = din.readInt(); total = price * unitPrice; display(description, price, unitPrice, total); } } catch(EOFException e) { //use this exception to stop the while loop //and exit the program System.exit(1); } catch(IOException e) { System.err.println(\"Error reading file: \" + e.toString()); System.exit(1); } } //display a record to screen public static void display(String d, double p, int u, double t) { System.out.println(d+\"\t\"+p+\"\t\"+u+\"\t\"+t); }}โปรแกรมของเราใช DataOutputStream และ DataInputStream เปนตวั สง ขอมลู ไปเก็บไวทไ่ี ฟล และเราใช for loop เปน ตวั กําหนดจาํ นวนครง้ั ของการเขยี นfout = new FileOutputStream(args[0]);dout = new DataOutputStream(fout);for(int i = 0; i < desc.length; i++) { dout.writeUTF(desc[i]); dout.writeDouble(prices[i]); dout.writeInt(unit[i]);}หลงั จากนนั้ เรากอ็ านขอ มูลออกมาจากไฟล ตามลําดบั ถาเราลองเปดดูไฟลท ่เี ก็บขอ มูลทเ่ี ราสรางขึ้น เราจะไมสามารถอา นได เพราะขอ มลู เหลา นถี้ กู เกบ็ อยใู นรปู แบบของ binary dataในการอา นขอ มลู ออกมาจากไฟลน ้นั เราใช while loop ในการอา นดวยการกาํ หนดใหเ ปน infinite loopโดยเราจะใช error ทเ่ี กิดขึ้นเปนตวั ยตุ ิการทาํ งานของ loop เนอื่ งจากวา เรารูวา ในการอา นขอมูลออกจากไฟลน น้ั ในท่ีสดุ เรากจ็ ะอา นเจอเคร่อื งหมายทบี่ ง บอกถงึ จดุ จบของไฟล (EOF) ซึ่งอาจเปน การเขียน codeท่ไี มคอยจะสวยเทาไร แตกท็ าํ ใหก ารทาํ งานของโปรแกรมเปน ไปตามทีเ่ ราตอ งการ ภาควิชาคอมพิวเตอรธุรกจิ วทิ ยาลัยฟารอ สี เทอรน
บทที่ 8 Streams I/O 224เร่ิมตนกับ Javafin = new FileInputStream(args[0]);din = new DataInputStream(fin);System.out.println(\"Description\tPrice\tUnit\tTotal\");while(true) { description = din.readUTF(); price = din.readDouble(); unitPrice = din.readInt(); total = price * unitPrice; display(description, price, unitPrice, total);}ผลลัพธท ่ีไดจ ากการ run คอื>java DataFile data.datDescription Price Unit TotalCoffee Mug 5.0 12 60.0Bookmark 2.0 50 100.0Note book 10.0 30 300.0จากผลลัพธท ่ีไดจะเห็นวา การจัดเรียงของขอ มูลทสี่ ง ไปยงั หนาจอ ยงั ดูไมเ รยี บรอ ยและสวยงามเทาไร ถาตองการใหก ารแสดงผลอยใู นรปู แบบทส่ี วยงาม เราจะตอ งสรา ง class ขึน้ มาใชเ องดวยการ extendsclass ท่เี ราสรางขน้ึ จาก class PrintWritter พรอ มกบั ทาํ การสรา ง method output() ทที่ ําการแสดงผลขอมลู ดว ยการจัดเรยี งทางขวา และทําการ override method print() และ println() เราจะสราง classFormatWriter ดังทีแ่ สดงใหด ูในโปรแกรมตวั อยางที่ไดดดั แปลงมาจากโปรแกรม DataFile.java//DataFile.java - Sequential Access file//-(modified for formatted output)import java.io.*;//for pretty printing (formatted - right justified)class FormatWriter extends PrintWriter { private int width = 10;//default constructorFormatWriter(Writer output) { super(output);}//constructor with specified widthFormatWriter(Writer out, int width) { super(out); this.width = width;}//set prefer output styleprivate void output(String str) { //calculate spaces int blanks = width - str.length(); //fill with blanks before actual data for(int i = 0; i < blanks; i++) super.print(' '); //print data super.print(str);} ภาควิชาคอมพิวเตอรธรุ กจิ วิทยาลยั ฟารอ ีสเทอรน
บทที่ 8 Streams I/O 225เร่ิมตน กับ Java//our print() and println() methods with different types of datapublic void print(String str) { output(str);}public void println(String str) { output(str); super.println();}public void print(double value) { output(String.valueOf(value));}public void print(int value) { output(String.valueOf(value));} public void println(double value) { this.print(value); super.println(); }}class DataFile {public static void main(String[] args) throws IOException {FileOutputStream fout; //file output streamDataOutputStream dout; //data output streamFileInputStream fin; //file input streamDataInputStream din; //data input stream//setting up data to be written to a fileString[] desc = {\"Coffee Mug\", \"Bookmark\", \"Note book\"};double[] prices = {5.00, 2.00, 10.00};int[] unit = {12, 50, 30};double[] total = new double[3];//open file for writingtry { fout = new FileOutputStream(args[0]); dout = new DataOutputStream(fout); for(int i = 0; i < desc.length; i++) { dout.writeUTF(desc[i]); dout.writeDouble(prices[i]); dout.writeInt(unit[i]); } dout.close();}catch(FileNotFoundException e) { System.err.println(\"Cannot open \" + args[0]); return;}catch(ArrayIndexOutOfBoundsException e) { System.err.println(\"Usage: DataFile file-name.\"); return;}//open file for input//reuse variables desc, prices, and unit//calculate total fro each data item ภาควิชาคอมพิวเตอรธ ุรกจิ วิทยาลัยฟารอีสเทอรน
บทที่ 8 Streams I/O 226เริ่มตนกบั Java try { fin = new FileInputStream(args[0]); din = new DataInputStream(fin); boolean endOfFile = false; //EOF marker int index = 0; //array index while(!endOfFile) { try { //store data in arrays before printing desc[index] = din.readUTF(); prices[index] = din.readDouble(); unit[index] = din.readInt(); total[index] = prices[index] * unit[index]; index++; } catch(EOFException e) { endOfFile = true; //stop while loop din.close(); //close the file } } //display data - right justified display(desc, prices, unit, total); } catch(IOException e) { System.err.println(\"Error reading file: \" + e.toString()); System.exit(1); }} //display a record to screen private static void display(String[] d, double[] p, int[] u, double[] t) { //create object from FileWriter with width of 12 FormatWriter out = new FormatWriter( new BufferedWriter( new FileWriter(FileDescriptor.out)), 12); //display header out.print(\"Description\"); out.print(\"Prices\"); out.print(\"Unit\"); out.println(\"Total\"); //display contents of arrays for(int i = 0; i < d.length; i++) { out.print(d[i]); out.print(p[i]); out.print(u[i]); out.println(t[i]); } out.close(); //close the stream }}เราสรา ง class FormatWriter ขนึ้ มาใหมจาก class PrintWriter พรอมท้ังกาํ หนดใหมี constructor 2โดยท่ตี วั แรกเปน default constructor ทม่ี ีหนาทเี่ รยี ก constructor ของ class PrintWriter สําหรับการกําหนดชอ งทางผา นออกของขอมูล สว นตวั ทสี่ องเรากาํ หนดใหม คี วามกวา งของเนอ้ื ท่ขี องขอ มลู ที่ตองการแสดงFormatWriter(Writer out, int width) { super(out); ภาควิชาคอมพิวเตอรธุรกิจ วิทยาลยั ฟารอสี เทอรน
บทท่ี 8 Streams I/O 227เรมิ่ ตนกับ Java this.width = width;}เราจะใช width ในการกําหนดจาํ นวนของชองวางท่เี ราตองการแสดงกอ นการแสดงขอมลู จริง เชน ถา เรากําหนดให width มีคาเปน 10 และขอ มลู ทต่ี อ งการแสดงมีคา เปน 2 ตวั อกั ษรเราก็จะทาํ การเขยี นชองวา งจาํ นวนเทา กับ 8 ตัว เสรจ็ แลว จึงเขยี นขอ มลู ตามชอ งวา งเหลาน้ี เราทาํ ไดด วยการเขียน methodoutput() ใหท าํ การคาํ นวณหาจํานวนชองวา งท่วี า เมื่อไดแลว จงึ สงไปยังหนา จอดว ยการเรียก print()ของ class PrintWriter หลังจากนัน้ จึงสงขอมลู จริงออกไปMethod output() จะถกู เรียกใชใ น print() และ println() ทเ่ี ราเขยี นข้นึ มารองรบั ขอมลู ตา งชนิดกัน ซ่งึในทีน่ ้เี รามขี อ มลู เพียงสามชนดิ คือ string double และ int การเขยี น print() และ println() กไ็ มย ากดงั น้ีpublic void print(int value) { output(String.valueOf(value));}public void println(double value) { this.print(value); super.println();}เราเพียงแตเรยี ก output() ดว ยคา ท่ีตอ งการแสดงผล แตเ ราจะตอ งเปลย่ี นคา นีใ้ หอ ยูใ นรูปแบบของstring กอนท่ีจะสงคา นีไ้ ปให output()เราเปลย่ี น display() ของเราใหมด วยการรับ parameter ท่ีเปน array แทนทจี่ ะเปนขอมลู เด่ียว ๆ เหมอื นทท่ี าํ กอนหนาน้ี พรอมทั้งประกาศตัวแปรจาก class FormatWriter ท่เี ราเขยี นขนึ้ ดังนี้private static void display(String[] d, double[] p, int[] u, double[] t) { //create object from FileWriter with width of 12 FormatWriter out = new FormatWriter( new BufferedWriter( new FileWriter(FileDescriptor.out)), 12); //display header out.print(\"Description\"); out.print(\"Prices\"); out.print(\"Unit\"); out.println(\"Total\"); //display contents of arrays for(int i = 0; i < d.length; i++) { out.print(d[i]); out.print(p[i]); out.print(u[i]); out.println(t[i]); } out.close(); //close the stream}เพอ่ื ใหก ารสง ขอ มลู ออกทาง standard output เปน ไปได เราตอ งสราง object จากสมาชกิ ทช่ี ื่อ outของ class FileDescriptor (สมาชิกของ FileDescriptor มอี ยสู ามตวั คอื in out และ err ซึง่ หมายถงึชองทางของ input output และ error ตามลําดบั ) หลังจากนนั้ เรากส็ ง object ตัวน้ไี ปใหBufferedWriter เพือ่ สราง buffer สาํ หรับรองรับขอมลู ทจ่ี ะเกดิ ขึน้ และ buffer ตวั นจ้ี ะเปน parameterตวั แรกทถี่ กู สงไปให constructor ของ FormatWriter และ 12 (ซึ่งเปน width) จะถกู สง ไปเปนparameter ตวั ท่สี อง ทาํ ให object out ของเราสามารถใชช อ งทางผา นออกได หลังจากนน้ั เรากแ็ สดงขอมูลดวยการเรียกใช print() และ println() ผาน object ตวั น้ี ภาควิชาคอมพิวเตอรธรุ กจิ วิทยาลัยฟารอีสเทอรน
บทท่ี 8 Streams I/O 228เร่มิ ตนกบั Javaในตัว main() ของเรา เราเปล่ยี นแปลง code ใหทาํ การอา นขอมลู มาเก็บไวใ น array สําหรบั การสงไปแสดงผลใน display() … …boolean endOfFile = false; //EOF markerint index = 0; //array indexwhile(!endOfFile) { try { //store data in arrays before printing desc[index] = din.readUTF(); prices[index] = din.readDouble(); unit[index] = din.readInt(); total[index] = prices[index] * unit[index]; index++; } catch(EOFException e) { endOfFile = true; //stop while loop din.close(); //close the file }}//display data - right justifieddisplay(desc, prices, unit, total); … …เรายังใช EOF error เปนตัวยตุ กิ ารทาํ งานของ loop เหมอื นเดมิ เพยี งแตแ ทนทจ่ี ะใชคา -1 เรากเ็ ลอื กที่จะใชต ัวแปร boolean เปนตวั กาํ หนดการทํางานของ loop แทนผลลัพธท ไี่ ดจ ากการ run คือ>java DataFile data.datDescription Prices Unit Total Coffee Mug 5.0 12.0 60.0 Bookmark 2.0 50.0 Note book 30.0 100.0 10.0 300.0เรายงั สามารถทีจ่ ะทําการแสดงผลท่อี ยใู นรปู แบบทเ่ี ราตอ งการ ไดอ กี หลายรปู แบบ ซึง่ กต็ องขน้ึ อยูกับผเู ขยี นโปรแกรมวา ตองการใหผ ลลัพธอ อกมาในลกั ษณะไหน ผูอา นคงตองใชเ วลาพอสมควรในการศึกษาและทดลองเขยี น code ทท่ี ําใหก ารแสดงผลมคี วามสวยงามมากขึ้นตวั อยา งตอไปนีจ้ ะเปน การแสดงถงึ ความแตกตา งในการใช writeUTF() กบั การใช writeChars()//WriteCharsAndWriteUTF.java - Differences between the to methodsimport java.io.*;class WriteCharsAndWriteUTF { public static void main(String[] args) throws IOException { File f = new File(args[0]); DataOutputStream out = new DataOutputStream( new BufferedOutputStream( new FileOutputStream(f))); out.writeUTF(\"If it does not work, blame the computer.\"); int utfSize = out.size(); ภาควิชาคอมพิวเตอรธ รุ กิจ วทิ ยาลัยฟารอ ีสเทอรน
บทที่ 8 Streams I/O 229เริม่ ตน กับ Java out.writeChars(\"If it does not work, blame the computer.\"); int charsSize = out.size() - utfSize; out.close(); System.out.println(\"writeUTF() writes \" + utfSize + \" bytes.\"); System.out.println(\"writeChars writes \" + charsSize + \" bytes.\"); }}ผลลพั ธท ่ไี ดจ ากการ run คือ>java WriteCharsAndWriteUTF difs.datwriteUTF() writes 42 bytes.writeChars writes 80 bytes.โปรแกรมของเราเขยี น string จาํ นวน 40 ตวั อักษรไปยงั ไฟลดวยการใช writeUTF() และ writeChars()จากผลของการ run จะเห็นวา writeUTF() ใชเ พยี งแค 1 byte ตอตัวอักษรบวกกับอกี 2 byte สําหรบั เกบ็ความยาวของ string สว น writeChars() ใช 2 byte ตอตวั อกั ษรการสรา งและใช Random-access fileตวั อยา งกอ นหนา นเี้ ราเขา หาขอ มูลในไฟลแ บบ กอ น-หลัง หรอื ที่เรยี กวา sequential access ซงึ่ เปนวิธีการทค่ี อนขา งทีจ่ ะใชเ วลามาก ถาขอมูลมอี ยูเปน จํานวนมาก มวี ธิ กี ารอีกวิธีหน่ึงทที่ าํ ใหก ารเขา หา หรือคน หาขอมูลทาํ ไดร วดเร็ว นน่ั กค็ อื การเขาหาแบบทไี่ มตองเขา หาตามลําดับ กอ น-หลงั หรือทเ่ี รยี กวาRandom-accessคําวา random-access ในที่นหี้ มายความวา ขอมลู ที่ถกู ดึงออก หรอื นาํ เขาไมม ีสว นเกี่ยวขอ งกบั ขอ มลู ท่ีถูกดึงออก หรือ นําเขากอนหนา นี้ เชน ในการคน หาเบอรโทรศัพทข องใครสกั คนผานทาง operator ผูคนหา ณ เวลาปจจบุ นั ไมมคี วามเก่ียวของหรอื ความสมั พนั ธใ ด ๆ กับการคน หาทผ่ี า นมากอนหนาในการจดั การกบั ขอ มลู ของ random-access file น้นั จะใชต ัวชีต้ ําแหนง (logical pointer) เปน ตวั กําหนดตาํ แหนงของการนาํ ขอ มูล เขา -ออก ซ่งึ การกําหนดตาํ แหนง จะคาํ นวณจากจดุ เรม่ิ ตนของไฟล (ไมใ ชตําแหนง ท่เี กบ็ จรงิ ในหนว ยความจาํ สํารอง) เราเรียกระยะทางจากจุดเริ่มตน น้วี า offset Java กาํ หนดการเขาหาตาํ แหนง เหลา น้ดี วยการใชค าํ สั่ง seek เพราะฉะน้นั ในการเขา หาขอมลู ถา เรารขู นาดของขอ มลู ท่ีตายตวั (fixed length record) เราก็สามารถเขาหาขอมลู ณ ตําแหนง p ดว ย p * lengthเราจะเริ่มตน ดว ยการสรา ง record ตวั อยาง โดยกาํ หนดให record ของเราประกอบดว ยช่ือ (first name)นามสกุล (last name)ปท ่เี กิด (year of birth)เงินเดือน (salary)เราจะกาํ หนดให record เกดิ จาก class ทมี่ สี มาชกิ ดังทก่ี ลา วขางตน พรอมกับ method ตา ง ๆ ท่ีเกีย่ วของกบั การทํางานกับ record นั้น ๆ//RandomAccessFileDemo.javaimport java.io.RandomAccessFile;import java.io.*;class RandomAccessFileDemo { public static void main(String[] args) throws IOException { //data to be written to a file ภาควิชาคอมพิวเตอรธุรกิจ วทิ ยาลยั ฟารอีสเทอรน
บทท่ี 8 Streams I/O 230เริม่ ตน กับ Java String[] names = {\"John\", \"Paul\", \"Stephen\", \"Hendrix\"}; String[] lasts = {\"Kawakami\", \"Collins\", \"Anderson\", \"McCoy\"}; int[] years = {1965, 1978, 1985, 1972}; double[] pays = {2400.0, 500.0, 5000.0, 9500.0}; //use to randomly select the position in a file //to write to/read from e.g. random[0] is at the second //position, random[2] is at the frist position etc. int []random = {2, 3, 0, 1}; //writing to a file RandomAccessFile f = new RandomAccessFile(\"emp.dat\", \"rw\"); try { for(int i = 0; i < 4; i++) { Employee emp = new Employee(names[i], lasts[i], years[i], pays[i]); emp.write(f, random[i]); } } catch(IOException e) { System.err.println(\"Error writing file\"); e.printStackTrace(); System.exit(1); } finally { if(f != null) { f.close(); } } //reading from a file RandomAccessFile fin = null; try { fin = new RandomAccessFile(\"emp.dat\", \"r\"); Employee em = new Employee(); System.out.println(\"Number of records = \" + em.size(fin) + \"\n\"); for(int i = 0; i < 4; i++) { em.read(fin, random[i]); System.out.print(\"Record #\" + (i+1) + \": \"); em.show(); } } catch(IOException e) { System.err.println(\"Error reading file\"); e.printStackTrace(); System.exit(1); } finally { if(fin != null) fin.close(); } }}โปรแกรมตวั อยา งของเราใช array 4 ตัวเปน ตัวเก็บ record ทีต่ อ งการเขยี นไปยงั ไฟล และเพอื่ เปนการแสดงใหเหน็ ถงึ การเขาหาไฟลใ นรูปแบบของความเปน random เราจึงใช array random เปนตวั กาํ หนดถงึ ตาํ แหนง ของ record ที่เราตองการจะเขยี น ซึ่งเราไดกําหนดให ภาควิชาคอมพิวเตอรธุรกจิ วิทยาลยั ฟารอีสเทอรน
บทท่ี 8 Streams I/O 231เร่มิ ตนกบั JavaRecord ตวั แรกเขียนในตําแหนงที่ 2 (random[0])Record ตวั ทีส่ องเขียนในตาํ แหนงที่ 3 (random[1])Record ตัวท่ีสามเขยี นในตาํ แหนง ท่ี 0 (random[2])Record ตวั ทสี่ ดุ ทา ยเขียนในตําแหนงที่ 1 (random[3])เราเก็บขอมูลเหลาน้ีดว ยการสรา ง object จาก class Employee ซ่ึงเปน class ที่ทําหนาทหี่ ลกั ในการเขยี น และอา น record ไปยัง และออกจากไฟล ตามลําดบั ซ่งึ มี code ดังน้ี//Employee.java - Simple record for random-access fileimport java.io.*;import java.io.RandomAccessFile;import java.lang.String;class Employee {private String firstName; //15 characters (30 bytes)private String lastName; //15 characters (30 bytes)private int year; //4 bytesprivate double salary; //8 bytesprivate static final int RECORD_SIZE = 72;//default constructorEmployee() { firstName = \"\"; lastName = \"\"; year = 0; salary = 0.0D;}//setting up fieldsEmployee(String firstName, String lastName, int year, double salary) { this.firstName = firstName; this.lastName = lastName; this.year = year; this.salary = salary;}//write a record to a file with a given positionpublic void write(RandomAccessFile file, int position) throws IOException { try { //locate a position file.seek((long)(position * RECORD_SIZE)); //write equal-length strings writeString(file, firstName, 15); writeString(file, lastName, 15); //write int and double file.writeInt(year); file.writeDouble(salary); } catch(IOException e) { System.err.println(\"Error writing file\"); e.printStackTrace(); System.exit(1); }} ภาควิชาคอมพิวเตอรธรุ กิจ วิทยาลยั ฟารอีสเทอรน
บทท่ี 8 Streams I/O 232เรม่ิ ตนกับ Java//reading a record from a file with a given positionpublic void read(RandomAccessFile file, int position) throws IOException { try { //locate position to read file.seek((long)(position * RECORD_SIZE)); //reading strings firstName = readString(file, 15); lastName = readString(file, 15); //reading int and double year = file.readInt(); salary = file.readDouble(); } catch(IOException e) { System.err.println(\"Error reading file\"); e.printStackTrace(); System.exit(1); }}//helper method to write a string with specified lengthpublic void writeString(DataOutput out, String s, int len) throws IOException { for(int i = 0; i < len; i++) { if(i < s.length()) out.writeChar(s.charAt(i)); else out.writeChar(0); }}//helper method to read a stringpublic String readString(DataInput in, int len) throws IOException { String s = \"\"; int i = 0; while(i < len) { char c = in.readChar(); if(c != 0) s += c; i++; } return s;}//number of records in filepublic int size(RandomAccessFile file) throws IOException{ int size = 0; try { size = (int)file.length() / RECORD_SIZE; } catch(IOException e) { System.err.println(\"Error reading file.\"); System.exit(1); } return size;} ภาควิชาคอมพิวเตอรธรุ กจิ วิทยาลยั ฟารอีสเทอรน
บทที่ 8 Streams I/O 233เร่มิ ตนกับ Java //display a record to screen public void show() { System.out.print(firstName + \" \"); System.out.print(lastName + \" \"); System.out.print(year + \" \"); System.out.println(salary); }}method หลกั ๆ ท่ีอยูใน class Employee คอืwrite(RandomAccessFile, position)read(RandomAccessFile, position)writeString(DataOutptu, String, length)readString(DataInput, length)size(RandomAccessFile)เน่ืองจากวาเราตอ งรขู นาดของ record ของเราวา มคี วามยาวเทา ไร เราจงึ จะสามารถทจ่ี ะเล่อื น logicalpointer ของเราไปยงั ตาํ แหนง ตาง ๆ ที่เราตอ งการได ดงั นน้ั เราจึงตอ งกําหนดขนาดของ record ภายในโปรแกรมของเราเพอื่ ใหก ารทาํ งานสะดวกขน้ึ ทงั้ นก้ี ารกาํ หนดขนาดกต็ องขนึ้ อยกู ับการเลือกใชว ธิ กี ารเขียนของเราวาใช method ตัวไหนเปน ตัวเขียน ถา เราใช writeUTF() เราก็ตองกาํ หนดขนาดแบบหนึง่แตถ า เราใช writeChars() เรากต็ องกําหนดขนาดอกี แบบหนง่ึ (ดูความแตกตางของการใชจ ากตาราง)สําหรบั โปรแกรมท่เี ราเขยี นขึน้ เรากําหนดใหข นาดของ record เปน 72 byte กเ็ พราะวาเราใชwriteChar() เปนตวั เขยี น ดังน้ัน ทั้งช่ือและนามสกุลใช 30 byte สว น year และ salary ใช 4 และ 8byte ตามลาํ ดบัหนาท่ขี อง write() คือ การนาํ ขอ มลู จาก field ตา ง ๆ ของ Employee object เขา สูไฟลในตําแหนงทไี่ ดกําหนดไว ดว ยการใช seek() เปน ตวั กําหนดตําแหนงfile.seek((long)(position * RECORD_SIZE));เราคูณ position ดว ย RECORD_SIZE ก็เพ่ือท่จี ะหาตาํ แหนง ไดถ ตู อ ง เน่อื งจากวา เรากําหนดให field 2ตวั แรกมคี วามยาวเทา กับ 15 ตวั อักษร (30 byte) หลังจากนนั้ เราก็เขียน field ทัง้ สองไปยงั ไฟลด ว ยการเรยี กใช writeString() ซ่งึ ทาํ หนาทใ่ี นการเขยี นจํานวน byte ที่ไดกาํ หนดไว และจะเขยี นคา 0 ในตําแหนงที่วา งอยู ถา ความยาวของ field ที่เขียนมนี อ ยกวา 15 ตัวอักษร//write equal-length stringswriteString(file, firstName, 15);writeString(file, lastName, 15);เมือ่ เราเขยี น field ทัง้ สองเสรจ็ เรากเ็ ขียน อีกสอง filed ท่เี หลอื ดว ย writeInt() และ writeDouble() โดยไมตอ งทําอะไรพิเศษ//write int and doublefile.writeInt(year);file.writeDouble(salary);code ของ writeString() กไ็ มยากอะไร เราเขยี นขอมูลทลี ะตวั ไปยงั ไฟลจ นกวา จะหมดขอ มลู ท่อี ยใู นstring ถาความยาวของ string ทเ่ี ราเขยี นมนี อ ยกวาความยาวทีก่ าํ หนดไว เรากเ็ ขียนคา 0 ลง ณ ตาํ แหนงน้ันpublic void writeString(DataOutput out, String s, int len) throws IOException { for(int i = 0; i < len; i++) { if(i < s.length()) out.writeChar(s.charAt(i)); else ภาควิชาคอมพิวเตอรธรุ กจิ วทิ ยาลยั ฟารอสี เทอรน
บทที่ 8 Streams I/O 234เร่ิมตน กับ Java } out.writeChar(0);}ในการอาน filed กลบั ออกมาก็เหมอื นกนั หลงั จากทใี่ ช seek() หาตาํ แหนง ไดแ ลว เรากเ็ รียกใชreadString() ในการอานสอง field แรก และใช readInt() กับ readDouble() ในการอา นอกี สอง field ที่เหลือCode ของ readString() กง็ า ย ๆ เราทําการอานทุกตวั แตจ ะเก็บไวเ ฉพาะตวั ทไ่ี มใ ช 0 เพราะ 0 เปน คา ที่เราใชแ ทนตาํ แหนงที่วางในการเขียน string เขา สูไ ฟลpublic String readString(DataInput in, int len) throws IOException { String s = \"\"; int i = 0; while(i < len) { char c = in.readChar(); if(c != 0) s += c; i++; } return s;}เราใช readChar() ในการอานขอ มลู แตล ะตวั และเราจะตรวจสอบวา คาทอี่ า นไดเ ปน 0 หรอื ไม ถา ไมเ ราก็เชอ่ื มคานเี้ ขาสู string s พรอ มกับเลื่อนไปอานขอมูลตัวตอไป ทาํ การอานและเชอ่ื มจนกวา จะหมดขอมลูสาํ หรับการหาจาํ นวนของ record ที่มอี ยใู นไฟลน้ันเราหาไดดว ยการใช ขนาดของไฟลห ารดว ยขนาดของrecord ทเ่ี ราไดก าํ หนดไวpublic int size(RandomAccessFile file) throws IOException{ int size = 0; try { size = (int)file.length() / RECORD_SIZE; } catch(IOException e) { System.err.println(\"Error reading file.\"); System.exit(1); } return size;}เราใช readChar() และ writeChar() ในการเขียนและอา นขอ มลู ดงั นัน้ เราตอ งคาํ นึงถึงจํานวนของ byteท่ีเราตอ งใชส ําหรับ record แตละตวั วาถกู ตอ งตามที่ไดก าํ หนดหรอื ไม สิง่ ที่ตอ งคาํ นึงถงึ ในเรื่องของการใช random access file ก็คอื field แตละ field ควรเปน field ทีม่ ขี นาดตายตัว เพราะจะทําใหการคน หาตาํ แหนงทาํ ไดอ ยา งถกู ตอ งผลลพั ธท เ่ี ราไดจ ากการ run คอืNumber of records = 4Record #1: John Kawakami 1965 2400.0Record #2: Paul Collins 1978 500.0Record #3: Stephen Anderson 1985 5000.0Record #4: Hendrix McCoy 1972 9500.0เรามาลองดตู ัวอยา งการปรบั ปรงุ ขอ มลู (update) ทีอ่ ยใู นไฟลก นั ภาควิชาคอมพิวเตอรธรุ กิจ วทิ ยาลยั ฟารอีสเทอรน
บทที่ 8 Streams I/O 235เริ่มตน กบั Javaการ update ขอมูลกค็ ลาย ๆ กับการเขยี นขอ มลู เขาสูไฟล เราตองหาตาํ แหนงของ record ทตี่ องการupdate ใหเ จอ ซ่ึงเม่อื เจอแลว การเปล่ียนแปลงขอ มลู กส็ ามารถทาํ ได เราเลอื กทจี่ ะเปลย่ี นแปลง recordของเราทกุ field เพ่ือเปน การแสดงถึงวธิ ีการในการ update แตในทางปฏบิ ตั จิ ริง คงไมมใี ครทาํ แบบน้ีคําสงั่ หลัก ๆ ในการ update คือf.seek((long)(recNum – 1) * em.recSize());em.write(f, recNum – 1);record ของเราถกู เก็บในตําแหนงทน่ี อยกวา ทีเ่ ปนจริงอยหู นึง่ คา ดงั น้นั การใช seek() เราจึงตอ งหักออกหนึง่ คา เราไดเขยี น method ขึน้ ใหมอกี หลายตวั เพอ่ื ชวยใหก ารกาํ หนด และ การดึงขอ มลู ออกจากrecord ทําไดโดยไมม ปี ญ หา method ทเี่ พม่ิ ข้ึนใน class Employee คอืpublic void setFirstName(String name) { firstName = name;}public void setLastName(String name) { lastName = name;}public void setSalary(double pay) { salary = pay;}public int recSize() { return RECORD_SIZE;}public void setYear(int y) { year = y;}สาํ หรบั กระบวนการหลัก ๆ ท่เี กี่ยวขอ งกบั การ update ขอมลู ก็ไมมีอะไรมาก หลังจากทรี่ บั ขอ มูลใหมมาจาก keyboard แลว พรอ มกับหาตําแหนงทต่ี อ งการ update เจอเราก็ทําการเขียนขอ มูลใหมท บั ลงทเี่ ดมิ …System.out.print(\"Enter record number: \");buffer = new BufferedReader(new InputStreamReader(System.in));str = buffer.readLine();recNum = Integer.parseInt(str);System.out.print(\"Enter first name: \");emp.setFirstName(buffer.readLine());System.out.print(\"Enter last name: \");emp.setLastName(buffer.readLine());System.out.print(\"Enter year of birth: \");emp.setYear(Integer.parseInt(buffer.readLine()));System.out.print(\"Enter salary: \");emp.setSalary(Double.parseDouble(buffer.readLine()));และ code สําหรบั การเขยี นทบั ท่ีเราไดพ ดู กอ นหนา นี้ …f.seek((long)(recNum – 1) * em.recSize());em.write(f, recNum – 1);ผลลพั ธข องการ run>java Update temp.dat ภาควิชาคอมพิวเตอรธุรกจิ วทิ ยาลยั ฟารอ ีสเทอรน
บทท่ี 8 Streams I/O 236เริ่มตนกบั JavaEnter record number: 1Enter first name: My nameEnter last name: Is changingEnter year of birth: 1965Enter salary: 2500Record #1: My name Is changing 1965 2500.0Record #2: Hendrix McCoy 1972 9500.0Record #3: John Kawakami 1965 2400.0Record #4: John Kowalski 1990 5400.0>java Update temp.datEnter record number: 3Enter first name: JennieEnter last name: SaiudomEnter year of birth: 1995Enter salary: 500Record #1: My name Is changing 1965 2500.0Record #2: Hendrix McCoy 1972 9500.0Record #3: Jennie Saiudom 1995 500.0Record #4: John Kowalski 1990 5400.0โปรแกรมตวั อยา งน้ี update ทกุ field ทีม่ ีอยูใน record ดวยการกาํ หนดขอมลู ทเี่ ขา มาใหมใ หกบั objectจาก class Employee เสร็จแลวกเ็ ขียน object ใหมนี้ลงในไฟล ซง่ึ การกระทําแบบนเ้ี ราไมส ามารถทีจ่ ะupdate บาง field ที่มีอยไู ด เราตอ ง update ทั้งหมด สาํ หรบั การ update เฉพาะ field ท่ตี อ งการนนั้ จะทงิ้ ไวใหเปนแบบฝก หัดทา ยบทตอไปตวั โปรแกรมทง้ั หมด มดี ังนคี้ ือ//Update.java - Updating Random-access fileimport java.io.RandomAccessFile;import java.io.*;import java.lang.Integer;class Update { public static void main(String[] args) throws IOException { RandomAccessFile f = null; Employee em = new Employee(); if(args.length < 1) { System.err.println(\"Usage: Update file-name\"); System.exit(1); } try { f = new RandomAccessFile(args[0], \"rw\"); int recNum = getNewData(f, em); //get new data updateData(f, recNum, em); //update record showData(f, args[0]); //display records } catch(IOException e) { System.err.println(\"Error processing file\"); e.printStackTrace(); System.exit(1); }} ภาควิชาคอมพิวเตอรธ ุรกจิ วิทยาลัยฟารอสี เทอรน
บทท่ี 8 Streams I/O 237เริม่ ตน กับ Java//get new data from keyboardprivate static int getNewData(RandomAccessFile f, Employee emp) throws IOException { BufferedReader buffer = null; InputStreamReader iStream = null; String str = \"\"; int recNum = 0; try { System.out.print(\"Enter record number: \"); buffer = new BufferedReader( new InputStreamReader(System.in)); str = buffer.readLine(); recNum = Integer.parseInt(str); System.out.print(\"Enter first name: \"); emp.setFirstName(buffer.readLine()); System.out.print(\"Enter last name: \"); emp.setLastName(buffer.readLine()); System.out.print(\"Enter year of birth: \"); emp.setYear(Integer.parseInt(buffer.readLine())); System.out.print(\"Enter salary: \"); emp.setSalary(Double.parseDouble(buffer.readLine())); } catch(IOException e) { System.err.println(\"Error readin file\"); e.printStackTrace(); System.exit(1); } return recNum; //record number}//update recordprivate static void updateData(RandomAccessFile f, int recNum, Employee em) throws IOException { try { //locate record f.seek((long)(recNum – 1) * em.recSize()); //write the whole record em.write(f, recNum – 1); } catch(IOException e) { System.err.println(\"Error writing file\"); e.printStackTrace(); System.exit(1); } finally { if(f != null) f.close(); }}//display all records in this fileprivate static void showData(RandomAccessFile f, String fName) throws IOException { try { f = new RandomAccessFile(fName, \"r\"); Employee em = new Employee(); ภาควิชาคอมพิวเตอรธ รุ กิจ วิทยาลัยฟารอสี เทอรน
บทที่ 8 Streams I/O 238เร่ิมตน กบั Java int num = em.size(f); for(int i = 0; i < 4; i++) { em.read(f, i); System.out.print(\"Record #\" + (i+1) + \":\"); em.show(); } } catch(IOException e) { System.err.println(\"Error reading file\"); e.printStackTrace(); System.exit(1); } finally { if(f != null) f.close(); } }}สรุปเราไดแ สดงตวั อยา งของการทาํ งานกับ text file และ binary file การอาน การเขยี น รวมไปถึงกระบวนการตา ง ๆ ทีเ่ กยี่ วของกับไฟล โดยสรปุ แลวเราไดพ ดู ถึง9 การตรวจสอบขอ มูลทเ่ี ก่ยี วของกบั ไฟลดวย File และ method ตาง ๆ เชน ifFile() canRead() เปน ตน9 การใช FileReader FileWriter FileInputStream FileOutputStream9 การใช DataOutputStream BufferedOutputStream9 การใช StreamTokenizer และ StringTokenizer9 การสรางและใชงาน text file9 การใช delimiter ในการอา นขอ มลู จาก text file9 การสรางและใชงาน binary file9 การสรา งและใชงาน Random-access file9 การ update ขอมูลใน random-access fileแบบฝก หดั1. จงเขียนโปรแกรมทท่ี าํ หนาทแ่ี สดงขอ มลู เก่ียวกบั ไฟลท ุกตัวทม่ี ีอยใู น directory นั้น ๆ เชน ถา เจอ ไฟลใหแ สดงถงึ ขนาด วันทส่ี ราง ถาเจอ directory ใหแ สดงถึงจาํ นวนไฟลท ี่มีอยใู น directory น้นั พรอ มกบั วนั ท่ี directory นี้ถูกสรางขึน้2. จงเขยี นโปรแกรมท่ี copy ขอ มูลจากไฟลห นง่ึ ไปยงั อีกไฟลห น่ึงผานทาง command-line3. จงเขียนโปรแกรมทสี่ รา งขอ มลู ชนดิ int ดวยการสมุ จาํ นวนเทากับ 100 ตัว หลังจากนั้นใหน ําขอ มลู ท้ังหมดไปเก็บไวใน text file โดยกาํ หนดใหจ าํ นวนของขอ มลู ตอแถวเทากับ 10 ตัว4. จงเขยี นโปรแกรมทีอ่ านขอมูลจากไฟลท ่ีเขยี นขนึ้ ในขอ 3 เสร็จแลวใหค าํ นวณหาผลรวมของขอ มลู ใน แตล ะแถว หลงั จากน้นั ใหเขียนขอมูลในแตล ะแถวรวมทั้งผลรวมท่ีหาไดล งในไฟลตัวเดิม (ขอมลู ใน แตละแถวจะมจี าํ นวนเทากบั 11 ตวั โดยทต่ี วั สดุ ทายในแถวเปน ผลรวมของขอมลู ในแถวนั้น)5. จงปรบั ปรงุ โปรแกรมที่เขยี นขน้ึ ในขอ 4 โดยใหมีการคํานวณหาคา เฉลี่ยของขอมลู ทุกตวั ในแนวตงั้ (column) นาํ ผลลพั ธทไี่ ดพรอ มทั้งขอมูลในแตล ะแถวไปเก็บไวในไฟลต ัวใหม6. จงเขยี นโปรแกรมทอี่ า นขอ มูลในรูปแบบที่กาํ หนดใหดานลางนี้ หลงั จากนั้นใหแ สดงขอ มูลท่ีอา นได ไปยังหนาจอ โดยไมตองแสดง delimiter ทีใ่ ช ใหก ําหนดจํานวนของขอมูลท่มี อี ยใู นไฟลจ าํ นวน เทา กับ 10 แถว ภาควิชาคอมพิวเตอรธ รุ กิจ วิทยาลัยฟารอีสเทอรน
บทท่ี 8 Streams I/O 239เรมิ่ ตน กบั Java John Longman+25.0+30.50+48.00 Peter Wang+35.00+50.00+98.00 … … Mike Kawakami+90.00+80.00+85.007. จงเขยี นโปรแกรมทส่ี รา ง binary file จาก class Student ทก่ี ําหนดใหน ี้ โดยทําการอา นขอ มลู เบ้อื งตน มาจาก keyboard ซ่งึ มีขอมลู ดงั นี้ ช่อื ตน เปน String จาํ นวน 15 ตัวอักษร นามสกุล เปน String จํานวน 15 ตัวอักษร เกรด เปน array ท่ีเกบ็ double เชน 85.0 90.5 ทง้ั นจี้ ํานวนของเกรดทีม่ ีอยจู ะมกี ต่ี วั ก็ได เสร็จแลว ใหท าํ การอา นขอ มูลกลับออกมาจากไฟล พรอมทงั้ คาํ นวณหาเกรดเฉลย่ี ของขอมูล (นักศึกษา) ทุกตัว เมือ่ ไดเ กรดเฉล่ียแลว ใหแสดงผลออกทางหนา จอ class Student { String firstName; String lastName; double[] grades; }8. จงเขียนโปรแกรมทสี่ รา ง random-access file จากโครงสรางของ Student ทีม่ ีอยใู นขอ 7 โดยให user เปนผกู ําหนดตาํ แหนงของ record ทต่ี อ งการเขียนเอง ใหเขยี น method ที่แสดงขอ มูลของ record (ทกี่ ําหนดมาจาก keyboard) ไปยงั หนา จอ9. จงปรบั ปรงุ โปรแกรม Update ในบทนใ้ี หมีการ update ไดเ ฉพาะ field ที่เปน salary เทานนั้10. จงปรบั ปรงุ โปรแกรมในขอ 6 ดว ยการเพ่มิ method ในการคาํ นวณหาผลรวมของขอ มลู ในแตละแถว หลงั จากน้ันใหน าํ ขอมลู ทงั้ หมดไปเก็บไวใ นไฟลตวั ใหม โดยใหผ ลรวมทหี่ าไดเ ปน ขอมูลตวั สดุ ทาย ในแถวนนั้ ภาควิชาคอมพิวเตอรธ ุรกิจ วิทยาลยั ฟารอีสเทอรน
ตารางตาง ๆ 240เรม่ิ ตนกบั Javaตารางตาง ๆตารางที่ 1 Unicode characters – 128 ตัวแรกdec hex char Dec hex char dec hex char dec hex char 66 0042 B 99 0063 c0 0000 <NUL> 33 0021 ! 67 0043 C 100 0064 d 68 0044 D 101 0065 e1 0001 <SOH> 34 0022 \" 69 0045 E 102 0066 f 70 0046 F 103 0067 g2 0002 <STX> 35 0023 # 71 0047 G 104 0068 h 72 0048 H 105 0069 i3 0003 <ETX> 36 0024 $ 73 0049 I 106 006A j 74 004A J 107 006B k4 0004 <EOT> 37 0025 % 75 004B K 108 006C l 76 004C L 109 006D m5 0005 <ENQ> 38 0026 & 77 004D M 110 006E n 78 004E N 111 006F o6 0006 <ACK> 39 0027 ' 79 004F O 112 0070 p 80 0050 P 113 0071 q7 0007 <BEL> 40 0028 ( 81 0051 Q 114 0072 r 82 0052 R 115 0073 s8 0008 <BS> 41 0029 ) 83 0053 S 116 0074 t 84 0054 T 117 0075 u9 0009 <HT> 42 002A * 85 0055 U 118 0076 v 86 0056 V 119 0077 w10 000A <LF> 43 002B + 87 0057 W 120 0078 x 88 0058 X 121 0079 y11 000B <VT> 44 002C ' 89 0059 Y 122 007A z 90 005A Z 123 007B {12 000C <FF> 45 002D - 91 005B [ 124 007C | 92 005C \ 125 007D }13 000D <CR> 46 002E . 93 005D ] 126 007E ~ 94 005E ^ 127 007F <DEL>14 000E <SO> 47 002F / 95 005F _ 128 0080 96 006015 000F <SI> 48 0030 0 97 0061 a 98 0062 b16 0010 <DLE> 49 0031 117 0011 <DC1> 50 0032 218 0012 <DC2> 51 0033 319 0013 <DC3> 52 0034 420 0014 <DC4> 53 0035 521 0015 <NAK> 54 0036 622 0016 <SYN> 55 0037 723 0017 <ETB> 56 0038 824 0018 <CAN> 57 0039 925 0019 <EM> 58 003A :26 001A <SUB> 59 003B ;27 001B <ESC> 60 003C <28 001C <FS> 61 003D =29 001D <GS> 62 003E >30 001E <RS> 63 003F ?31 001F <US> 64 0040 @32 0020 blank 65 0041 Aตารางท่ี 2 Constants ท่ใี ชบ อย ๆ ในโปรแกรม ความหมายชือ่ คา ของ e (natural logarithm)Math.E คา ของ PI (3.145)Math.PI infinity ที่เปนลบFloat/Double.NEGATIVE_INFINITY infinity ทีเ่ ปนบวกFloat/Double.POSITIVE_INFINITY ไมใชตวั เลขFloat/Double.NaN คาสูงสุดByte/Short/Integer/Long/Float/Double.MAXIMUM_VALUE คา ตํ่าสดุByte/Short/Integer/Long/Float/Double.MINIMUM_VALUE ทางเขาขอ มลู (keyboard)System.in ทางออกขอมูล (จอ)System.out ทางออกของ error (จอ)System.err ภาควิชาคอมพิวเตอรธ รุ กจิ วทิ ยาลัย ฟารอสี เทอรน
ตารางตาง ๆ 241เริม่ ตน กบั Javaตารางที่ 3 Numeric Types คาตาํ่ สดุ คาสูงสุดชนิด bit คาเบอ้ื งตน -128 127byte 8 0 -32768 32767short 16 0 -2147483648 2147483647int 32 0 -9223372036854775808 9223372036854775807long 64 0 1.40129846432481707e-45f 3.40282346638528860e+38ffloat 32 0.0f 4.94065645841246544e-324 1.79769313486231570e+308double 64 0.0dตารางที่ 4 Escape Sequences ความหมายEscape Sequence back space\b tab\t LF (ข้ึนบรรทัดใหม)\n FF (ขึน้ หนาใหม)\f CR (กลบั ไปยงั จดุ เริ่มตน)\r double quote\\" single quote\' back slash\\ Unicode จาก \u ตามดว ยเลขฐาน 16 ส่ีตวั\uxxxx เลขฐานแปด เชน \101\xxxตารางที่ 5 Format Syntax สาํ หรบั ตวั เลขform คา ผลลัพธ ความหมาย 0 –ใชแ ทน digit0000 314 0314 # - ใชแทน digit . - จุดทศนยิ ม###0 314 314 , - ใชแ บงกลุมตัวเลข % - คูณตวั เลขดว ย 100 เพ่อื แสดง %##0.0# 3.10 3.1 \u00A4 – แสดงสกลุ เงินตาม locale ของประเทศ#,##0 3000 3,000##0.00% 3.1415 314.15%\u00A4##0 314 $314 ภาควิชาคอมพิวเตอรธรุ กิจ วิทยาลัย ฟารอสี เทอรน
Search
Read the Text Version
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246