"مرحبًا! في درس اليوم، سنواصل حديثنا حول تدفقات الإدخال والإخراج في Java ( Java I/O ). هذا ليس الدرس الأول حول هذا الموضوع، وبالتأكيد لن يكون الأخير :) لأنه في بعض الأحيان، توفر لغة Java العديد من الطرق للعمل مع الإدخال/الإخراج. هناك عدد لا بأس به من الفئات التي تنفذ هذه الوظيفة، لذلك قمنا بتقسيمها إلى عدة دروس - لذلك لن تشعر بالارتباك من البداية :) في الماضي الدروس التي تطرقنا إليها
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!
الآن هذا مختلف! لا تنس هذه الميزة عند استخدام فئات الإدخال/الإخراج. كان هناك وقت قضيت فيه ساعات في أداء المهام، وأجهد عقلي لساعات، محاولًا فهم كيفية اختفاء بياناتي من الملفات :) وبالطبع، تمامًا كما هو الحال مع فئات الإدخال/الإخراج الأخرى، لا تنس استخدام هذه 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
:) لقد واجهنا بالفعل تدفقات مخزنة، لذا حاول التخمين (أو التذكر) قبل مواصلة القراءة :) هناك حاجة إلى التدفقات المخزنة بشكل أساسي لتحسين الإدخال/الإخراج. يعد الوصول إلى مصدر بيانات، مثل القراءة من ملف، عملية مكلفة من حيث الأداء، كما أن الوصول إلى ملف لقراءة كل بايت يعد إهدارًا. ولهذا السبب 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