File
. על עבודתו תוכלו לקרוא כאן . אבל ב-Java 7, יוצרי השפה החליטו לשנות את אופן העבודה שלהם עם קבצים וספריות. זה נבע מהעובדה שלכיתה File
היו מספר חסרונות. לדוגמה, לא הייתה לו שיטה copy()
שתאפשר לך להעתיק קובץ ממיקום אחד לאחר (תכונה נחוצה לכאורה). בנוסף, לכיתה File
היו די הרבה שיטות שהחזירו boolean
-ערכים. אם מתרחשת שגיאה, שיטה כזו מחזירה false במקום לזרוק חריג, מה שמקשה מאוד על אבחון שגיאות וקביעת הסיבות שלהן. במקום מחלקה בודדת, File
הופיעו עד 3 מחלקות: Paths
, Path
ו Files
. ובכן, אם לדייק, Path
זה ממשק, לא מחלקה. בואו נבין איך הם שונים זה מזה ומדוע כל אחד מהם נחוץ. נתחיל בדבר הכי קל - Paths
.
שבילים
Paths
היא מחלקה פשוטה מאוד עם שיטה סטטית אחת get()
. הוא נוצר אך ורק כדי להשיג אובייקט מסוג מהמחרוזת או ה-URI שעברו Path
. אין לו פונקציונליות אחרת. הנה דוגמה לעבודתו:
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
, בואו נבין מה זה Path
ולמה הוא נחוץ :)
נָתִיב
Path
, בגדול, הוא אנלוגי שעוצב מחדש של ה- File
. הרבה יותר קל לעבוד איתו מאשר עם File
. ראשית , שיטות שימוש רבות (סטטיות) הוסרו ממנו והועברו למחלקה Files
. שנית , Path
הוזמנו ערכי ההחזר של השיטות. בכיתה, File
השיטות החזירו את זה String
, את זה boolean
, את זה File
- לא היה קל להבין. לדוגמה, הייתה שיטה getParent()
שהחזירה את נתיב האב עבור הקובץ הנוכחי כמחרוזת. אבל במקביל הייתה שיטה getParentFile()
שהחזירה את אותו הדבר, אבל בצורה של אובייקט File
! ברור שזה מיותר. לכן, בממשק, Path
השיטה getParent()
ושיטות אחרות לעבודה עם קבצים פשוט מחזירות אובייקט Path
. אין ערימות של אפשרויות - הכל קל ופשוט. אילו שיטות שימושיות יש לו Path
? הנה כמה מהם ודוגמאות לעבודותיהם:
-
getFileName()
- מחזיר את שם הקובץ מהנתיב; -
getParent()
- מחזירה את ספריית "האב" ביחס לנתיב הנוכחי (כלומר, הספרייה שנמצאת גבוה יותר בעץ הספריות); -
getRoot()
- מחזירה את ספריית "שורש"; כלומר, זה שנמצא בראש עץ הספריות; -
startsWith()
,endsWith()
— בדוק אם הנתיב מתחיל/מסתיים בנתיב שעבר: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שימו לב איך השיטה עובדת
endsWith()
. הוא בודק אם הנתיב הנוכחי מסתיים בנתיב שעבר . זה על הנתיב , ולא על סט הדמויות .השווה את התוצאות של שתי שיחות אלה:
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")); } }
פלט מסוף:
שקר
אמתאתה צריך להעביר את הנתיב המלא למתודה
endsWith()
, ולא רק קבוצה של תווים: אחרת התוצאה תמיד תהיה false , גם אם הנתיב הנוכחי מסתיים למעשה ברצף כזה של תווים (כמו במקרה של "estFile.txt " בדוגמה למעלה).בנוסף, ישנה
Path
קבוצת שיטות המפשטת את העבודה עם נתיבים מוחלטים (מלאים) ויחסיים .
-
boolean isAbsolute()
- מחזירה true אם הנתיב הנוכחי הוא מוחלט: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()); } }
פלט מסוף:
נָכוֹן
-
Path normalize()
- "מנרמל" את הנתיב הנוכחי, מסיר ממנו אלמנטים מיותרים. אתה אולי יודע שמערכות הפעלה פופולריות משתמשות לעתים קרובות בתווי "." כאשר מציינים נתיבים. ("ספרייה נוכחית") ו-".." (ספריית אב). לדוגמה: " ./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)); } }
פלט מסוף:
שם משתמש\Desktop\testFile.txt
Path
היא די גדולה. אתה יכול למצוא את כולם בתיעוד של Oracle . נעבור לסקירה Files
.
קבצים
Files
- זוהי מחלקת שירות שבה הועברו שיטות סטטיות מהמחלקה File
. Files
- זה בערך כמו Arrays
או Collections
, רק זה עובד עם קבצים, ולא עם מערכים ואוספים :) הוא מתמקד בניהול קבצים וספריות. באמצעות שיטות סטטיות Files
, אנו יכולים ליצור, למחוק ולהזיז קבצים וספריות. עבור פעולות אלה נעשה שימוש בשיטות 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 {
//file creation
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 directory
Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
System.out.println("Was the directory successfully created?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));
//move file from desktop to testDirectory. You need to move with the name of the file in the folder!
testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);
System.out.println("Is our file left 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")));
//remove 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()
). פלט מסוף: האם הקובץ נוצר בהצלחה? true האם הספרייה נוצרה בהצלחה? true האם הקובץ שלנו עדיין על שולחן העבודה? false האם הקובץ שלנו הועבר ל-testDirectory? true האם הקובץ עדיין קיים? שֶׁקֶר שים לב:בדיוק כמו שיטות ממשק 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 {
//file creation
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 directory
Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
System.out.println("Was the directory successfully created?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));
//copy the file from the desktop to the directory testDirectory2.
testFile1 = Files.copy(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt"), REPLACE_EXISTING);
System.out.println("Is our file left on the desktop?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
System.out.println("Has our file been copied to testDirectory?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
}
}
פלט מסוף: האם הקובץ נוצר בהצלחה? true האם הספרייה נוצרה בהצלחה? true האם הקובץ שלנו עדיין על שולחן העבודה? true האם הקובץ שלנו הועתק ל-testDirectory? true עכשיו אתה יכול להעתיק קבצים באופן תכנותי! :) אבל הכיתה Files
מאפשרת לך לא רק לנהל את הקבצים בעצמם, אלא גם לעבוד עם התוכן שלו. כדי לכתוב נתונים לקובץ, יש לו שיטה , ולקריאה - write()
עד 3:, 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);
}
}
}
פלט קונסולה: אני זוכר רגע נפלא: הופעת לפני, כמו חזון חולף, כמו גאון של יופי טהור. בנוחות רבה! :) תכונה זו הופיעה ב-Java 7. ב-Java 8 הופיע Stream API , שהוסיף כמה אלמנטים של תכנות פונקציונלי ל-Java. כולל יכולות ניהול קבצים עשירות יותר. תאר לעצמך שיש לנו משימה: מצא את כל השורות בקובץ שמתחילות במילה "איך", המר אותן לאותיות גדולות ופלט אותן לקונסולה. איך ייראה פתרון באמצעות מחלקה 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("How")) {
String upper = s.toUpperCase();
result.add(upper);
}
}
for (String s: result) {
System.out.println(s);
}
}
}
פלט קונסולה: כמו חזון בצום, כמו גאון של יופי טהור. נראה שעשינו את זה, אבל אתה לא חושב שלמשימה כל כך פשוטה התברר שהקוד שלנו היה קצת... מילולי? באמצעות Java 8 Stream API הפתרון נראה הרבה יותר אלגנטי:
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("How"))
.map(String::toUpperCase)
.collect(Collectors.toList());
result.forEach(System.out::println);
}
}
השגנו את אותה תוצאה, אבל עם הרבה פחות קוד! יתרה מכך, אי אפשר לומר שהפסדנו ב"קריאות". אני חושב שאתה יכול להגיב בקלות על מה שהקוד הזה עושה, גם אם אתה לא מכיר את ה- Stream API. אבל בקיצור, Stream הוא רצף של אלמנטים שעליהם אתה יכול לבצע פונקציות שונות. אנו מקבלים את אובייקט ה-Stream מהשיטה Files.lines()
ולאחר מכן מיישמים עליו 3 פונקציות:
-
באמצעות השיטה,
filter()
אנו בוחרים רק את השורות מהקובץ שמתחילות ב"איך". -
אנו עוברים על כל השורות הנבחרות בשיטה
map()
ומביאים כל אחד מהם ל- UPPER CASE. -
אנו משלבים את כל השורות המתקבלות תוך
List
שימוש ב-collect()
.
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("Required file found!");
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());
}
}
פלט מסוף: הקובץ הדרוש נמצא! C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt הקובץ הדרוש נמצא! C:\Users\Username\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt הקובץ הדרוש נמצא! C:\Users\Username\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt מעולה, עשינו את זה! :) אם אתה רוצה לדעת יותר על walkFileTree()
, אני ממליץ לך על מאמר זה . אתה יכול גם להשלים משימה קטנה - החלף אותה SimpleFileVisitor
במשימה רגילה FileVisitor
, יישם את כל 4 השיטות והמצא מטרה לתוכנית זו. לדוגמה, אתה יכול לכתוב תוכנית שתתעד את כל הפעולות שלך: הצג את שם הקובץ או התיקיה בקונסולה לפני/אחרי הכניסה אליהן. זה הכל - נתראה מאוחר יותר! :)
GO TO FULL VERSION