CodeGym/Java блог/Случаен/Java файлове, път
John Squirrels
Ниво
San Francisco

Java файлове, път

Публикувано в групата
здрасти Днес ще говорим за работа с файлове и директории. Вече знаете How да управлявате съдържанието на file: посветихме много уроци на това :) Мисля, че ви е лесно да запомните няколко класа, използвани за тези цели. В днешния урок ще говорим конкретно за управление на файлове: създаване, преименуване и т.н. Преди Java 7 всички подобни операции се извършваха с помощта на класа File . Можете да прочетете за това тук . Но в Java 7 създателите на езика решиха да променят начина, по който работим с файлове и директории. Това се случи, защото класът File имаше няколко недостатъка. Например, нямаше метода copy() , който би ви позволил да копирате файл от едно място на друго (привидно важна възможност). В допълнение, наФайловият клас имаше доста методи, които връщаха булеви стойности. Когато има грешка, такъв метод връща false. Не създава изключение, което прави много трудно идентифицирането на грешките и диагностицирането на причините за тях. На мястото на единичния клас File се появиха 3 класа: Paths , Path и Files . Е, за да бъдем точни, Path е интерфейс, а не клас. Нека да разберем How се различават един от друг и защо имаме нужда от всеки от тях. Нека започнем с най-простото: Пътища .

пътеки

Paths е много прост клас с един статичен метод: get() . Създаден е единствено за получаване на обект Path от предадения низ or 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");
   }
}
Не е най-сложният клас, нали? :) Е, имаме и този тип Path . Нека да разберем Howво е Path и защо е необходимо :)

Пътека

Path като цяло е преработен аналог на класа File . С него се работи много по-лесно от File . Първо , много полезни (статични) методи бяха извадени и преместени в класа Files . Второ , беше наложен ред върху върнатите стойности на методите на интерфейса Path . В класа File методите върнаха or String , or boolean , or File . Не беше лесно да го разбера. Например, имаше метод getParent() , който върна низ, представляващ родителския път на текущия файл. Но имаше и аметод getParentFile() , който върна същото нещо, но под формата на File обект! Това явно е излишно. Съответно винтерфейса Path методът getParent() и други методи за работа с файлове просто връщат обект Path . Няма купчина опции - всичко е лесно и просто. Кои са някои от полезните методи, които има Path ? Ето някои от тях и примери How работят:
  • getFileName() : връща името на file от пътя;

  • getParent() : връща "родителската" директория на текущия път (с други думи, директорията, разположена непосредствено по-горе в дървото на директорията);

  • getRoot() : връща "root" директорията, т.е. директорията в горната част на дървото на директориите;

  • 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

    Обърнете внимание How работи методът 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"));
       }
    }

    Конзолен изход:

    false
    true

    На метода endsWith() трябва да бъде подаден истински път, а не просто набор от знаци: в противен случай резултатът винаги ще бъде false, дори ако текущият път наистина завършва с тази последователност от знаци (Howто е случаят с "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());
       }
    }

    Конзолен изход:

    true
  • Path normalize() : "нормализира" текущия път, като премахва ненужните елементи от него. Може би знаете, че в популярните операционни системи символите "." (текуща директория) и ".." (родителска директория) често се използват за обозначаване на пътища. Например " ./Pictures/dog.jpg " означава, че текущата директория има папка "Pictures", която от своя страна съдържа файл "dog.jpg".

    Вижте тук. Ако път, използващ "." or ".." се появява във вашата програма, методът 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

Пълният списък с методи на Path е доста дълъг. Можете да ги намерите всички в documentацията на Oracle . Сега ще преминем към разглеждане на файлове .

файлове

Files е помощен клас, който съдържа статичните методи, взети от класа File . Файловете са сравними с масиви or колекции . Разликата е, че работи с файлове, а не с масиви or колекции :) Фокусира се върху управлението на файлове и директории. Използвайки статичните методи на класа Files , можем да създаваме, изтриваме и преместваме файлове и директории. Тези операции се извършват с помощта наметодите createFile() (за директории createDirectory() ), move() и delete() . Ето How да ги използвате:
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() ). След това преместваме file ( метод Files.move() ) от работния плот в тази нова папка и накрая изтриваме file ( метод 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
Вече знаете How да копирате файлове програмно! :) Разбира се, Filesкласът ви позволява не само да управлявате самия файл, но и да работите с неговото съдържание. Има write()метода за запис на данни във файл и всички 3 метода за четене на данни: read(), readAllBytes(), и readAllLines() Ще се спрем подробно на последния. Защо този? Тъй като има много интересен тип връщане: List<String>! Тоест, връща ни списък с всички редове във file. Разбира се, това прави много удобна работата със съдържанието на file, тъй като целият файл, ред по ред, може например да бъде показан на конзолата с помощта на обикновен цикъл 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.
Мисията е изпълнена, но не мислите ли, че за толкова проста задача нашият code се оказа малко... многословен? Използвайки 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);
   }
}
Постигнахме същия резултат, но с много по-малко code! Още повече, че никой не може да каже, че сме загубor "четимост". Мисля, че можете лесно да коментирате Howво прави този code, дори без да сте запознати с API на потока. Накратко, потокът е последователност от елементи, над които можете да извършвате различни операции. Получаваме Stream обект от Files.lines()метода и след това прилагаме 3 функции към него:
  1. Използваме filter()метода, за да изберем само тези редове от file, които започват с "As".

  2. Преминаваме през всички избрани редове с помощта на map()метода и преобразуваме всеки от тях в ГЛАВНИ БУКВИ.

  3. Използваме 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 структурата е подобна, но директорията / еФайлове, път - 2 основната. Сега си представете, че трябва да започнем от основната директория , прегледайте всички негови папки и подпапки и намерете файлове с определено съдържание. Ще търсим файлове, които съдържат реда „Това е файлът, от който се нуждаем!“ Ще вземем папката „testFolder“, която е на работния плот, като главна директория. Ето съдържанието му: Файлове, път - 3Папките level1-a и level1-b също съдържат папки: Файлове, път - 4Файлове, път - 5В тези "папки от второ ниво" няма папки, а само отделни файлове: Файлове, път - 6Файлове, път - 7На 3-те file със съдържанието, от което се нуждаем, умишлено са дадени обяснителни имена: FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt. Това са точно файловете, които трябва да намерим с помощта на Java. Как да направим това? На помощ ни идва много мощен метод за обхождане на файлово дърво: Files.walkFileTree (). Ето Howво трябва да направим. Първо, имаме нужда от FileVisitor. FileVisitorе специален интерфейс, в който са описани методите за обхождане на файлово дърво. По-конкретно, там ще поставим логиката за четене на съдържанието на файл и проверка дали съдържа необходимия ни текст. Ето How 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(). Тук ние определяме Howво трябва да се направи с всеки файл във всяка директория. Ако имате нужда от по-сложна логика за преминаване през файловата структура, трябва да напишете своя собствена реализация на FileVisitor. Ще трябва да внедрите още 3 метода в този клас:
  • preVisitDirectory(): логиката за изпълнение преди влизане в папка;

  • visitFileFailed(): логиката за изпълнение, ако даден файл не може да бъде посетен (без достъп or по други причини);

  • postVisitDirectory(): логиката за изпълнение след влизане в папка.

Не се нуждаем от изпълнение на такава логика, така че сме добре с SimpleFileVisitor. Логиката вътре в visitFile()метода е доста проста: прочетете всички редове във file, проверете дали съдържат необходимото съдържание и ако е така, отпечатайте абсолютния път на конзолата. Единственият ред, който може да ви затрудни е този:
return FileVisitResult.CONTINUE;
Всъщност това е много просто. Тук ние просто описваме Howво трябва да направи програмата, след като файлът бъде посетен и всички необходими операции са извършени. В нашия случай искаме да продължим да обикаляме дървото, така че избираме опцията CONTINUE. Но, алтернативно, може да имаме различна цел: instead of да намираме всички файлове, които съдържат „Това е файлът, от който се нуждаем“, намерете само един такъв файл . След това програмата трябва да приключи. В този случай нашият code ще изглежда точно по същия начин, но instead of break ще има:
return FileVisitResult.TERMINATE;
Е, нека стартираме нашия code и да видим дали работи.
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, заменете всичките 4 метода и измислете своя собствена цел за програмата. Например, можете да напишете програма, която регистрира всичките си действия: показва името на file or папката преди or след въвеждането им. Това е всичко за сега. Ще се видим скоро! :)
Коментари
  • Популярен
  • Нов
  • Стар
Трябва да сте влезли, за да оставите коментар
Тази страница все още няма коментари