مسارات
المسارات عبارة عن فئة بسيطة جدًا لها طريقة ثابتة واحدة: get() . تم إنشاؤه فقط للحصول على كائن مسار من السلسلة أو URI الذي تم تمريره. ليس لديه وظيفة أخرى. وهنا مثال على ذلك في العمل:import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) {
Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
}
}
ليست الفئة الأكثر تعقيدا، أليس كذلك؟ :) حسنًا، لدينا أيضًا هذا النوع من المسار . دعونا معرفة ما هو المسار ولماذا هو مطلوب :)
طريق
المسار ، بشكل عام، هو نظير مُعاد تصميمه لفئة الملف . العمل معه أسهل بكثير من التعامل مع File . أولاً ، تم إزالة العديد من الأساليب المساعدة (الثابتة) ونقلها إلى فئة الملفات . ثانيًا ، تم فرض الترتيب على القيم المرجعة لطرق واجهة المسار . في فئة الملف ، أعادت الطرق إما سلسلة أو منطقية أو ملف . لم يكن من السهل معرفة ذلك. على سبيل المثال، كان هناك أسلوب getParent() الذي قام بإرجاع سلسلة تمثل المسار الأصلي للملف الحالي. ولكن كان هناك أيضًا طريقة getParentFile () التي أعادت نفس الشيء ولكن في شكل كائن ملف ! ومن الواضح أن هذا زائدة عن الحاجة. وفقًا لذلك، في واجهة المسار ، تقوم طريقة getParent() والطرق الأخرى للعمل مع الملفات ببساطة بإرجاع كائن المسار . لا يوجد كومة من الخيارات - كل شيء سهل وبسيط. ما هي بعض الطرق المفيدة التي يمتلكها Path ؟ فيما يلي بعض منها وأمثلة على كيفية عملها:-
getFileName() : يُرجع اسم الملف من المسار؛
-
getParent() : يُرجع الدليل "الأصل" للمسار الحالي (بمعنى آخر، الدليل الموجود مباشرة أعلى شجرة الدليل)؛
-
getRoot() : يُرجع الدليل "الجذر"، أي الدليل الموجود أعلى شجرة الدليل؛
-
startWith() و endWith() : تحقق مما إذا كان المسار يبدأ/ينتهي بالمسار الذي تم تمريره:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); Path fileName = testFilePath.getFileName(); System.out.println(fileName); Path parent = testFilePath.getParent(); System.out.println(parent); Path root = testFilePath.getRoot(); System.out.println(root); boolean endWithTxt = testFilePath.endsWith("Desktop\\testFile.txt"); System.out.println(endWithTxt); boolean startsWithLalala = testFilePath.startsWith("lalalala"); System.out.println(startsWithLalala); } }
إخراج وحدة التحكم:
testFile.txt C:\Users\Username\Desktop C:\ true false
انتبه إلى كيفية عمل طريقة endWith() . يتحقق مما إذا كان المسار الحالي ينتهي بالمسار الذي تم تمريره . على وجه التحديد، سواء كان ذلك في المسار ، وليس في السلسلة التي تم تمريرها .
قارن نتائج هاتين النداءتين:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath.endsWith("estFile.txt")); System.out.println(testFilePath.endsWith("Desktop\\testFile.txt")); } }
إخراج وحدة التحكم:
false true
يجب أن يتم تمرير طريقة endWith() مسارًا حقيقيًا، وليس مجرد مجموعة من الأحرف: وإلا فستكون النتيجة خاطئة دائمًا، حتى لو كان المسار الحالي ينتهي بالفعل بهذا التسلسل من الأحرف (كما هو الحال مع "estFile.txt" "في المثال أعلاه).
بالإضافة إلى ذلك، يحتوي المسار على مجموعة من الأساليب التي تبسط العمل مع المسارات المطلقة (الكاملة) والنسبية .
-
boolean isAbsolute() يُرجع صحيحًا إذا كان المسار الحالي مطلقًا:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath.isAbsolute()); } }
إخراج وحدة التحكم:
true
-
تطبيع المسار () : "تطبيع" المسار الحالي، وإزالة العناصر غير الضرورية منه. ربما تعلم أن الرموز "." موجودة في أنظمة التشغيل الشائعة. (الدليل الحالي) و".." (الدليل الأصلي) غالبًا ما يُستخدمان لتعيين المسارات. على سبيل المثال، " ./Pictures/dog.jpg " يعني أن الدليل الحالي يحتوي على مجلد "صور"، والذي يحتوي بدوره على ملف "dog.jpg".
انظر هنا. إذا كان المسار يستخدم "." أو ".." في برنامجك، سيقوم التابع Normalize() بإزالتها وإنتاج مسار لا يحتوي عليها:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path path5 = Paths.get("C:\\Users\\Java\\.\\examples"); System.out.println(path5.normalize()); Path path6 = Paths.get("C:\\Users\\Java\\..\\examples"); System.out.println(path6.normalize()); } }
إخراج وحدة التحكم:
C:\Users\Java\examples C:\Users\examples
-
Path relativize() : يحسب المسار النسبي بين المسار الحالي والمسار الذي تم تمريره.
على سبيل المثال:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath1 = Paths.get("C:\\Users\\Users\\Users\\Users"); Path testFilePath2 = Paths.get("C:\\Users\\Users\\Users\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath1.relativize(testFilePath2)); } }
إخراج وحدة التحكم:
Username\Desktop\testFile.txt
ملفات
الملفات هي فئة أدوات مساعدة تحتوي على الأساليب الثابتة المأخوذة من فئة الملف . الملفات قابلة للمقارنة مع المصفوفات أو المجموعات . الفرق هو أنه يعمل مع الملفات، وليس المصفوفات أو المجموعات :) فهو يركز على إدارة الملفات والدلائل. باستخدام الأساليب الثابتة لفئة الملفات ، يمكننا إنشاء الملفات والأدلة وحذفها ونقلها. يتم تنفيذ هذه العمليات باستخدام أساليب createFile() (للدلائل، createDirectory() )، و move() ، و delete() . وإليك كيفية استخدامها:import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
public class Main {
public static void main(String[] args) throws IOException {
// Create a file
Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
System.out.println("Was the file created successfully?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
// Create a directory
Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
System.out.println("Was the directory created successfully?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));
// Move the file from the desktop to the testDirectory directory. When you move a folder, you need to indicate its name in the folder!
testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);
System.out.println("Did our file remain on the desktop?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
System.out.println("Has our file been moved to testDirectory?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
// Delete a file
Files.delete(testFile1);
System.out.println("Does the file still exist?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
}
}
هنا نقوم أولاً بإنشاء ملف ( طريقة Files.createFile() ) على سطح المكتب. ثم نقوم بإنشاء مجلد في نفس الموقع ( طريقة Files.createDirectory() ). بعد ذلك، نقوم بنقل الملف ( طريقة Files.move() ) من سطح المكتب إلى هذا المجلد الجديد، وأخيراً نقوم بحذف الملف ( طريقة Files.delete() ). إخراج وحدة التحكم:
Was the file created successfully?
true
Was the directory created successfully?
true
Did our file remain on the desktop?
false
Has our file been moved to testDirectory?
true
Does the file still exist?
false
ملحوظة:مثل أساليب الواجهة Path
، تقوم العديد من أساليب الفئة Files
بإرجاعPath
كائن. تأخذ معظم أساليب الفصل Files
أيضًا Path
الكائنات كمدخلات. هنا Paths.get()
ستكون الطريقة هي مساعدك المخلص – استفد منها جيدًا. ما هو الشيء الآخر المثير للاهتمام Files
؟ ما افتقرت إليه الطبقة القديمة File
حقًا هو copy()
الطريقة! تحدثنا عنها في بداية هذا الدرس. الآن حان الوقت لمقابلته!
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
public class Main {
public static void main(String[] args) throws IOException {
// Create a file
Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
System.out.println("Was the file created successfully?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
// Create a directory
Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
System.out.println("Was the directory created successfully?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));
// Copy the file from the desktop to the testDirectory2 directory.
testFile1 = Files.copy(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt"), REPLACE_EXISTING);
System.out.println("Did our file remain on the desktop?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
System.out.println("Was our file copied to testDirectory?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
}
}
إخراج وحدة التحكم:
Was the file created successfully?
true
Was the directory created successfully?
true
Did our file remain on the desktop?
true
Was our file copied to testDirectory?
true
الآن أنت تعرف كيفية نسخ الملفات برمجياً! :) بالطبع، Files
لا يتيح لك الفصل إدارة الملف نفسه فحسب، بل يتيح لك أيضًا التعامل مع محتوياته. يحتوي على write()
طريقة كتابة البيانات في ملف، وجميع الطرق الثلاثة لقراءة البيانات: read()
, readAllBytes()
, readAllLines()
وسنتناول الطريقة الأخيرة بالتفصيل. لماذا هذا واحد؟ لأنه يحتوي على نوع إرجاع مثير جدًا للاهتمام: List<String>
! أي أنه يُرجع لنا قائمة بجميع الأسطر الموجودة في الملف. بالطبع، هذا يجعل من السهل جدًا العمل مع محتويات الملف، لأن الملف بأكمله، سطرًا تلو الآخر، يمكن، على سبيل المثال، عرضه على وحدة التحكم باستخدام حلقة عادية for
:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
public class Main {
public static void main(String[] args) throws IOException {
List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);
for (String s: lines) {
System.out.println(s);
}
}
}
إخراج وحدة التحكم:
I still recall the wondrous moment:
When you appeared before my sight,
As though a brief and fleeting omen,
Pure phantom in enchanting light.
مريحة للغاية! :) ظهرت هذه القدرة في Java 7. ظهرت Stream API في Java 8. وهي تضيف بعض عناصر البرمجة الوظيفية إلى Java. بما في ذلك قدرات معالجة الملفات الأكثر ثراءً. تخيل أن لدينا المهمة التالية: العثور على جميع الأسطر التي تبدأ بالكلمة "As"، وتحويلها إلى أحرف كبيرة، وعرضها على وحدة التحكم. كيف سيبدو الحل باستخدام Files
الفصل في Java 7؟ شيء من هذا القبيل:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
public class Main {
public static void main(String[] args) throws IOException {
List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);
List<String> result = new ArrayList<>();
for (String s: lines) {
if (s.startsWith("As")) {
String upper = s.toUpperCase();
result.add(upper);
}
}
for (String s: result) {
System.out.println(s);
}
}
}
إخراج وحدة التحكم:
AS THOUGH A BRIEF AND FLEETING OMEN,
PURE PHANTOM IN ENCHANTING LIGHT.
تم إنجاز المهمة، ولكن ألا تعتقد أنه بالنسبة لمثل هذه المهمة البسيطة، تبين أن الكود الخاص بنا كان قليلاً... مطولاً؟ باستخدام Stream API الخاص بـ Java 8، يبدو الحل أكثر أناقة:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) throws IOException {
Stream<String> stream = Files.lines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"));
List<String> result = stream
.filter(line -> line.startsWith("As"))
.map(String::toUpperCase)
.collect(Collectors.toList());
result.forEach(System.out::println);
}
}
لقد حققنا نفس النتيجة، ولكن مع كود أقل بكثير! علاوة على ذلك، لا يمكن لأحد أن يقول إننا فقدنا "سهولة القراءة". أعتقد أنه يمكنك التعليق بسهولة على ما يفعله هذا الرمز، حتى دون أن تكون على دراية بـ Stream API. باختصار، الدفق عبارة عن سلسلة من العناصر التي يمكنك إجراء عمليات مختلفة عليها. نحصل على كائن Stream من Files.lines()
الطريقة، ثم نطبق عليه 3 وظائف:
-
نستخدم الطريقة
filter()
لتحديد تلك الأسطر من الملف التي تبدأ بـ "As" فقط. -
نحن نسير عبر جميع الأسطر المحددة باستخدام
map()
الطريقة ونحول كل منها إلى أحرف كبيرة. -
نستخدم
collect()
الطريقة لتجميع كل الأسطر المستلمة في ملفList
.
AS THOUGH A BRIEF AND FLEETING OMEN,
PURE PHANTOM IN ENCHANTING LIGHT.
الآن دعونا نعود إلى خبزنا وزبدتنا، أي الملفات :) القدرة الأخيرة التي سنأخذها في الاعتبار اليوم هي السير عبر شجرة الملفات . في أنظمة التشغيل الحديثة، غالبًا ما تبدو بنية الملف وكأنها شجرة: فهي تحتوي على جذر وهناك فروع يمكن أن يكون لها فروع أخرى، وما إلى ذلك. الجذر والفروع عبارة عن أدلة. على سبيل المثال، قد يكون الدليل " С:// " هو الجذر. ويتضمن فرعين: " C://Downloads " و" C://Users ". ولكل فرع من هذه الفروع فرعان: " C://Downloads/Pictures "، " C://Downloads/Video "، " C://Users/JohnSmith "، " C://Users/Pudge2005 ". وهذه الفروع بدورها لها فروع أخرى وغيرها، ولهذا نسميها شجرة. في Linux، البنية متشابهة، لكن الدليل / هو الجذر. تخيل الآن أننا بحاجة إلى البدء من الدليل الجذر، والتجول عبر جميع مجلداته ومجلداته الفرعية، والعثور على الملفات التي تحتوي على محتوى معين. سنبحث عن الملفات التي تحتوي على السطر "هذا هو الملف الذي نحتاجه!" سنأخذ المجلد "testFolder"، الموجود على سطح المكتب، باعتباره الدليل الجذر. فيما يلي محتوياته: تحتوي مجلدات المستوى 1-أ والمستوى 1-ب أيضًا على مجلدات: لا توجد مجلدات في "مجلدات المستوى الثاني"، فقط ملفات فردية: تم إعطاء الملفات الثلاثة التي تحتوي على المحتويات التي نحتاجها أسماء توضيحية عمدًا: FileWeNeed1. txt، FileWeNeed2.txt، FileWeNeed3.txt. هذه هي بالضبط الملفات التي نحتاج إلى العثور عليها باستخدام Java. كيف نفعل ذلك؟ هناك طريقة قوية جدًا لاجتياز شجرة الملفات لمساعدتنا: Files.walkFileTree ()
. وهنا ما يتعين علينا القيام به. أولا، نحن بحاجة إلى FileVisitor
. FileVisitor
هي واجهة خاصة يتم من خلالها وصف طرق اجتياز شجرة الملفات. على وجه الخصوص، هذا هو المكان الذي سنضع فيه المنطق لقراءة محتويات الملف والتحقق مما إذا كان يحتوي على النص الذي نحتاجه. وهنا ما FileVisitor
يبدو لدينا:
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
public class MyFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
List<String> lines = Files.readAllLines(file);
for (String s: lines) {
if (s.contains("This is the file we need")) {
System.out.println("We found a file we need!");
System.out.println(file.toAbsolutePath());
break;
}
}
return FileVisitResult.CONTINUE;
}
}
في هذه الحالة، يرث صفنا SimpleFileVisitor
. هذه فئة تنفذ FileVisitor
، حيث نحتاج إلى تجاوز طريقة واحدة فقط: visitFile()
. نحدد هنا ما يجب القيام به مع كل ملف في كل دليل. إذا كنت بحاجة إلى منطق أكثر تعقيدًا لاجتياز بنية الملف، فيجب عليك كتابة تطبيقك الخاص لـ FileVisitor
. ستحتاج إلى تنفيذ 3 طرق أخرى في هذا الفصل:
-
preVisitDirectory()
: المنطق الذي يجب تنفيذه قبل الدخول إلى المجلد؛ -
visitFileFailed()
: منطق التنفيذ إذا تعذرت زيارة الملف (عدم الوصول، أو لأسباب أخرى)؛ -
postVisitDirectory()
: المنطق الذي سيتم تنفيذه بعد الدخول إلى المجلد.
SimpleFileVisitor
. المنطق داخل visitFile()
الطريقة بسيط للغاية: اقرأ جميع الأسطر الموجودة في الملف، وتحقق مما إذا كانت تحتوي على المحتوى الذي نحتاجه، وإذا كان الأمر كذلك، فاطبع المسار المطلق على وحدة التحكم. السطر الوحيد الذي قد يسبب لك صعوبة هو هذا:
return FileVisitResult.CONTINUE;
في الواقع، هذا بسيط جدًا. نحن هنا نصف ببساطة ما يجب أن يفعله البرنامج بعد زيارة الملف وإجراء جميع العمليات الضرورية. في حالتنا، نريد مواصلة اجتياز الشجرة، لذلك نختار الخيار CONTINUE
. ولكن، بدلاً من ذلك، قد يكون لدينا هدف مختلف: بدلاً من البحث عن جميع الملفات التي تحتوي على "هذا هو الملف الذي نحتاجه"، ابحث عن ملف واحد فقط من هذا القبيل . وبعد ذلك، يجب أن ينتهي البرنامج. في هذه الحالة، سيبدو الكود الخاص بنا هو نفسه تمامًا، ولكن بدلاً من الفاصل سيكون هناك:
return FileVisitResult.TERMINATE;
حسنًا، فلنقم بتشغيل الكود الخاص بنا ونرى ما إذا كان يعمل أم لا.
import java.io.IOException;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Files.walkFileTree(Paths.get("C:\\Users\\Username\\Desktop\\testFolder"), new MyFileVisitor());
}
}
إخراج وحدة التحكم:
We found a file we need!
C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt
We found a file we need!
C:\Users\Username\Desktop\testFolder\level1-a\level2-a-a\FileWeNeed2.txt
We found a file we need!
C:\Users\Username\Desktop\testFolder\level1-b\level2-b-b\FileWeNeed3.txt
ممتاز! انها عملت! :) يمكنك أيضًا قبول هذا التحدي الصغير: استبدله SimpleFileVisitor
بآخر عادي FileVisitor
، وتجاوز جميع الطرق الأربع، وتوصل إلى غرضك الخاص من البرنامج. على سبيل المثال، يمكنك كتابة برنامج يسجل جميع إجراءاته: اعرض اسم الملف أو المجلد قبل أو بعد إدخاله. هذا كل شئ حتى الان. اراك قريبا! :)
GO TO FULL VERSION