"היי! בשיעור של היום, נמשיך בשיחתנו על זרמי קלט ופלט ב-Java ( Java I/O ). זה לא השיעור הראשון בנושא זה, ובוודאי לא יהיה האחרון :)
שכן קורה, שפת Java מספקת דרכים רבות לעבוד עם I/O. יש לא מעט מחלקות שמיישמות את הפונקציונליות הזו, אז חילקנו אותן למספר שיעורים - כדי שלא תתבלבלו מההתחלה :) בעבר נגענו בשיעורים
הנה איך זה נראה לקרוא נתונים מקובץ באמצעות

BufferedReader
, כמו גם בכיתות InputStream
המופשטות OutputStream
ובכמה צאצאים. היום נשקול 3 כיתות חדשות: FileInputStream
, FileOutputStream
, ו BufferedInputStream
.
המחלקה FileOutputStream
המטרה העיקרית שלFileOutputStream
המחלקה היא לכתוב בייטים לקובץ. שום דבר מסובך :) FileOutputStream
הוא אחד המימושים של OutputStream
המחלקה המופשטת. בבנאי, אובייקטים של מחלקה זו לוקחים את הנתיב לקובץ היעד (שם יש לכתוב את הבתים) או אובייקט File
. נבחן דוגמאות של כל אחד מהם:
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\Username\\Desktop\\test.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
בעת יצירת File
האובייקט העברנו את הנתיב הרצוי לבנאי. אנחנו לא צריכים ליצור אותו מראש: אם הוא לא קיים, התוכנית תיצור אותו. אתה יכול גם להסתדר בלי ליצור אובייקט נוסף, פשוט להעביר מחרוזת עם הנתיב:
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt");
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
התוצאה בשני המקרים תהיה זהה. אנחנו יכולים לפתוח את הקובץ שלנו ולראות שם את הדברים הבאים:
Hi! Welcome to CodeGym — The best site for would-be programmers!
אבל יש כאן ניואנס אחד. נסה להריץ את הקוד מהדוגמה למעלה מספר פעמים ברציפות. לאחר מכן עיין בקובץ וענה על השאלה הזו: כמה שורות יש לו? רק אחד. אבל הרצת את הקוד כמה פעמים. מסתבר שהנתונים מוחלפים בכל פעם - הישן מוחלף בחדש. מה עושים אם זה לא מתאים לנו ואנחנו צריכים לכתוב ברצף לקובץ? מה אם נרצה לכתוב את הברכה שלנו לקובץ שלוש פעמים ברציפות? הכל מאוד פשוט. מכיוון שהשפה לא יכולה לדעת איזו התנהגות אנחנו צריכים בכל מקרה, המעצב FileOutputStream
יכול לקחת פרמטר נוסף - boolean append
. אם הערך שלו נכון, הנתונים ייכתבו לסוף הקובץ. אם הוא שקר (וברירת המחדל הוא שקר), כל נתונים ישנים יימחקו ויוחלף בנתונים חדשים. בוא נבדוק זאת על ידי הפעלת הקוד ששונה שלוש פעמים:
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt", true);
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!\r\n";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
תוכן הקובץ:
Hi! Welcome to CodeGym — The best site for would-be programmers!
Hi! Welcome to CodeGym — The best site for would-be programmers!
Hi! Welcome to CodeGym — The best site for would-be programmers!
עכשיו זה אחרת! אל תשכח את התכונה הזו בעת שימוש בשיעורי I/O. הייתה תקופה שבה ביליתי שעות במשימות, ביליתי שעות במוח, בניסיון להבין איך הנתונים שלי נעלמים מקבצים :) וכמובן, בדיוק כמו בשיעורי I/O אחרים, אל תשכחו להשתמש close()
בשיטה לפנות משאבים.
המחלקה FileInputStream
למטרהFileInputStream
ההפוכה - קריאת בתים מקובץ. בדיוק כמו FileOutputStream
יורשת OutputStream
, מחלקה זו נובעת מהמחלקה InputStream
המופשטת. נכתוב כמה שורות טקסט בקובץ " test.txt " שלנו:
"So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters"

FileInputStream
:
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt");
int i;
while((i=fileInputStream.read())!= -1){
System.out.print((char)i);
}
}
}
אנו קוראים בית אחד מהקובץ, ממירים את הבייטים הנקראים לתווים ומציגים אותם בקונסולה. והנה פלט הקונסולה:
So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters
המחלקה BufferedInputStream
אני חושב, בהתחשב בידע משיעורי העבר, אתה יכול להגיד בקלות למה אנחנו צריכים את השיעורBufferedInputStream
ואיזה יתרונות יש לו בהשוואה FileInputStream
:) כבר נתקלנו בזרמים מאוחסנים, אז נסו לנחש (או לזכור) לפני שאתם ממשיכים לקרוא :) נחוצים זרמים ממוגנים בעיקר כדי לייעל את ה-I/O. גישה למקור נתונים, כמו קריאה מקובץ, היא פעולה יקרה מבחינת ביצועים ולגשת לקובץ כדי לקרוא כל בייט זה בזבוז. זו הסיבה BufferedInputStream
שקורא נתונים לא בייט אחד בכל פעם, אלא בבלוקים, ומאחסן אותם באופן זמני במאגר מיוחד. זה מאפשר לנו לייעל את התוכנית על ידי הפחתת מספר הפעמים שאנו ניגשים לקובץ. בוא נראה איך זה נראה:
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 200);
int i;
while((i = bufferedInputStream.read())!= -1){
System.out.print((char)i);
}
}
}
כאן יצרנו אובייקט BufferedInputStream
. הבנאי שלו לוקח מופע של InputStream
המחלקה או כל אחד מהצאצאים שלה, כך גם FileInputStream
יעשה. כארגומנט נוסף, הוא לוקח את גודל המאגר בבתים. הודות לטיעון זה, הנתונים ייקראו כעת מהקובץ לא בייט אחד בכל פעם, אלא 200 בייט בכל פעם! תארו לעצמכם כמה צמצמנו את מספר הגישה לקבצים. כדי להשוות ביצועים, אתה יכול לקחת קובץ טקסט גדול (מספר מגה בייט של טקסט) ולהשוות את משך הזמן שלוקח באלפיות שניות לקרוא ולפלט לקונסולה באמצעות FileInputStream
ו BufferedInputStream
. הנה קוד שמדגים את שתי האפשרויות:
public class Main {
public static void main(String[] args) throws IOException {
Date date = new Date();
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\textBook.rtf");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
int i;
while((i = bufferedInputStream.read())!= -1){
System.out.print((char)i);
}
Date date1 = new Date();
System.out.println((date1.getTime() - date.getTime()));
}
}
public class Main {
public static void main(String[] args) throws IOException {
Date date = new Date();
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\26951280.rtf");
int i;
while((i = fileInputStream.read())!= -1){
System.out.print((char)i);
}
Date date1 = new Date();
System.out.println((date1.getTime() - date.getTime()));
}
}
כשקראתי קובץ של 1.5 מגה-בייט במחשב שלי, FileInputStream
השלמתי את העבודה תוך ~3500 מילישניות, אבל BufferedInputStream
הצלחתי אותה תוך ~1700 מילישניות. כפי שאתה יכול לראות, הזרם המאוחסן ייעל את העבודה, חתך אותה לשניים! :) נמשיך ללמוד שיעורי I/O - נתראה בקרוב!
GO TO FULL VERSION