CodeGym /Java Blogu /Rastgele /Java Dosyaları, Yol
John Squirrels
Seviye
San Francisco

Java Dosyaları, Yol

grupta yayınlandı
MERHABA! Bugün dosya ve dizinlerle çalışmaktan bahsedeceğiz. Dosya içeriklerini nasıl yöneteceğinizi zaten biliyorsunuz: buna birçok ders ayırdık :) Bu amaçlar için kullanılan birkaç sınıfı hatırlamanın kolay olduğunu düşünüyorum. Bugünün dersinde özellikle dosya yönetimi hakkında konuşacağız: oluşturma, yeniden adlandırma vb. Java 7'den önce, bu tür tüm işlemler File sınıfı kullanılarak gerçekleştiriliyordu . Burada okuyabilirsiniz . Ancak Java 7'de, dilin yaratıcıları dosya ve dizinlerle çalışma şeklimizi değiştirmeye karar verdi. Bu, File sınıfının birkaç dezavantajı olduğu için oldu . Örneğin, bir dosyayı bir konumdan diğerine kopyalamanıza izin veren (görünüşe göre temel bir yetenek olan) copy() yöntemine sahip değildi . ek olarakFile sınıfı, boole değerleri döndüren epeyce yönteme sahipti . Bir hata olduğunda, böyle bir yöntem false döndürür. Bir istisna oluşturmaz, bu da hataları tanımlamayı ve nedenlerini teşhis etmeyi çok zorlaştırır. Tek File sınıfının yerine 3 sınıf belirdi: Paths , Path ve Files . Kesin olmak gerekirse, Path bir sınıf değil, bir arayüzdür. Birbirlerinden nasıl farklı olduklarını ve neden her birine ihtiyacımız olduğunu anlayalım. En basitinden başlayalım: Yollar .

Yollar

Yollar, tek bir statik yöntemi olan çok basit bir sınıftır: get() . Yalnızca iletilen dizeden veya URI'den bir Yol nesnesi almak için oluşturuldu . Başka bir işlevi yoktur. İşte iş yerinde bir örnek:

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");
   }
}
En karmaşık sınıf değil, değil mi? :) Pekala, bu Yol tipine de sahibiz . Yolun ne olduğunu ve neden gerekli olduğunu bulalım :)

Yol

Path , genel olarak, File sınıfının yeniden tasarlanmış bir benzeridir. Çalışmak File ile çalışmaktan çok daha kolaydır . İlk olarak , birçok yardımcı (statik) yöntem çıkarıldı ve Files sınıfına taşındı. İkincisi , Path arabiriminin yöntemlerinin dönüş değerlerine düzen getirildi . File sınıfında , yöntemler bir String veya bir boolean veya bir File döndürdü . Bunu anlamak kolay olmadı. Örneğin, geçerli dosyanın üst yolunu temsil eden bir dize döndüren bir getParent() yöntemi vardı. Ama aynı zamanda birgetParentFile() yöntemi, aynı şeyi bir File nesnesi biçiminde döndürdü! Bu açıkça gereksizdir. Buna göre, Path arabiriminde, getParent() yöntemi ve dosyalarla çalışmaya yönelik diğer yöntemler yalnızca bir Path nesnesi döndürür. Yığın seçenek yok — her şey kolay ve basit. Path'in sahip olduğu yararlı yöntemlerden bazıları nelerdir ? İşte bunlardan bazıları ve nasıl çalıştıklarına dair örnekler:
  • getFileName() : yoldan dosya adını döndürür;

  • getParent() : geçerli yolun "ana" dizinini (başka bir deyişle, dizin ağacının hemen yukarısında bulunan dizini) döndürür;

  • getRoot() : "kök" dizini, yani dizin ağacının en üstündeki dizini döndürür;

  • StartsWith() , endWith() : yolun geçirilen yolla başlayıp bitmediğini kontrol edin:

    
    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);
       }
    }
    

    Konsol çıktısı:

    
    testFile.txt
    C:\Users\Username\Desktop
    C:\
    true
    false
    

    endWith() yönteminin nasıl çalıştığına dikkat edin. Geçerli yolun geçen yolla bitip bitmediğini kontrol eder . Spesifik olarak, geçirilen dizede değil , yolda olup olmadığı .

    Bu iki aramanın sonuçlarını karşılaştırın:

    
    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"));
       }
    }
    

    Konsol çıktısı:

    
    false
    true
    

    endWith () yöntemine yalnızca bir dizi karakter değil, gerçek bir yol iletilmelidir: aksi takdirde, geçerli yol gerçekten bu karakter dizisiyle bitse bile ("estFile.txt'de olduğu gibi) sonuç her zaman yanlış olacaktır. "yukarıdaki örnekte).

    Ayrıca Path , mutlak (tam) ve göreli yollarla çalışmayı basitleştiren bir grup yönteme sahiptir .

Bu yöntemlere bir göz atalım:
  • boolean isAbsolute(), geçerli yol mutlaksa true değerini döndürür:

    
    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());
       }
    }
    

    Konsol çıktısı:

    
    true
    
  • Path normalize() : geçerli yolu "normalleştirir" ve gereksiz öğeleri ondan kaldırır. Popüler işletim sistemlerinde "" sembollerinin olduğunu biliyor olabilirsiniz. (geçerli dizin) ve ".." (ana dizin) genellikle yolları belirtmek için kullanılır. Örneğin, " ./Pictures/dog.jpg ", geçerli dizinin bir "Pictures" klasörüne sahip olduğu ve bunun da bir "dog.jpg" dosyası içerdiği anlamına gelir.

    Buraya bak. "." veya programınızda ".." görünürse, normalize() yöntemi bunları kaldırır ve onları içermeyen bir yol oluşturur:

    
    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());
       }
    }
    

    Konsol çıktısı:

    
    C:\Users\Java\examples
    C:\Users\examples
    
  • Path relativize() : geçerli ve geçirilen yol arasındaki göreli yolu hesaplar.

    Örneğin:

    
    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));
       }
    }
    

    Konsol çıktısı:

    
    Username\Desktop\testFile.txt
    

Yol yöntemlerinin tam listesi oldukça uzundur. Hepsini Oracle belgelerinde bulabilirsiniz . Şimdi Files'ı ele almaya geçeceğiz .

Dosyalar

Files, File sınıfından alınan statik yöntemleri tutan bir yardımcı program sınıfıdır. Files, Arrays veya Collections ile karşılaştırılabilir. Aradaki fark, diziler veya koleksiyonlarla değil, dosyalarla çalışmasıdır :) Dosyaları ve dizinleri yönetmeye odaklanır. Files sınıfının statik yöntemlerini kullanarakdosya ve dizin oluşturabilir, silebilir ve taşıyabiliriz. Bu işlemler, createFile() (dizinler için, createDirectory() ), move() ve delete() yöntemlerikullanılarak gerçekleştirilirBunları nasıl kullanacağınız aşağıda açıklanmıştır:

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")));
   }
}
Burada önce masaüstünde bir dosya ( Files.createFile() yöntemi) oluşturuyoruz . Ardından aynı konumda bir klasör oluşturuyoruz ( Files.createDirectory() yöntemi). Bundan sonra, dosyayı ( Files.move() yöntemi) masaüstünden bu yeni klasöre taşıyoruz ve son olarak dosyayı siliyoruz ( Files.delete() yöntemi). Konsol çıktısı:

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
Not:arayüzün yöntemleri gibi Path, sınıfın birçok yöntemi de Filesbir nesne döndürürPath . Sınıfın yöntemlerinin çoğu nesneleri Filesde girdi olarak alır Path. Burada Paths.get()yöntem sadık yardımcınız olacak - onu iyi kullanın. Başka ne ilginç Files? Eski Filesınıfın gerçekten eksik olduğu şey bir copy()yöntem! Bu dersin başında bunun hakkında konuştuk. Şimdi onunla tanışma zamanı!

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")));
   }
}
Konsol çıktısı:

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
Artık dosyaları programlı olarak nasıl kopyalayacağınızı biliyorsunuz! :) Tabii ki, Filessınıf sadece bir dosyayı yönetmenize değil, aynı zamanda içeriğiyle de çalışmanıza izin verir. write()Bir dosyaya veri yazma yöntemine ve verileri okumak için 3 yöntemin tümüne sahiptir : read(), readAllBytes(), ve readAllLines() Sonuncusu üzerinde ayrıntılı olarak duracağız. Neden o? Çünkü çok ilginç bir dönüş tipi var: List<String>! Yani, bize dosyadaki tüm satırların bir listesini döndürür. Tabii ki, bu, dosya içerikleriyle çalışmayı çok kolaylaştırır, çünkü tüm dosya, örneğin, sıradan bir fordöngü kullanılarak konsolda satır satır görüntülenebilir:

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);
       }
   }
}
Konsol çıktı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.
Süper uygun! :) Bu yetenek Java 7'de ortaya çıktı. Stream API, Java 8'de ortaya çıktı. İşlevsel programlamanın bazı öğelerini Java'ya ekler. Daha zengin dosya işleme yetenekleri dahil. Aşağıdaki görevimiz olduğunu hayal edin: "As" kelimesiyle başlayan tüm satırları bulun, BÜYÜK HARF'e çevirin ve konsolda görüntüleyin. Sınıfı kullanan bir çözüm FilesJava 7'de nasıl görünürdü? Bunun gibi bir şey:

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);
       }
   }
}
Konsol çıktısı:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Görev tamamlandı, ancak bu kadar basit bir görev için kodumuzun biraz... ayrıntılı olduğunu düşünmüyor musunuz? Java 8'in Akış API'sini kullanan çözüm çok daha şık görünüyor:

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);
   }
}
Aynı sonucu çok daha az kodla elde ettik! Dahası, hiç kimse "okunabilirliği" kaybettiğimizi söyleyemez. Stream API'ye aşina olmadan bile bu kodun ne yaptığı hakkında kolayca yorum yapabileceğinizi düşünüyorum. Kısacası, Akış , üzerinde çeşitli işlemler gerçekleştirebileceğiniz bir dizi öğedir. Yöntemden bir Stream nesnesi alıyoruz Files.lines()ve ona 3 işlev uyguluyoruz:
  1. filter()Dosyadan yalnızca "As" ile başlayan satırları seçmek için yöntemi kullanıyoruz .

  2. Yöntemi kullanarak seçilen tüm satırları gözden geçiriyoruz map()ve her birini BÜYÜK HARF'e çeviriyoruz.

  3. collect()Alınan tüm satırları bir List.

Aynı çıktıyı alıyoruz:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Şimdi ekmek ve tereyağımıza, yani dosyalarımıza dönelim :) Bugün ele alacağımız son yetenek, bir dosya ağacında yürümek . Modern işletim sistemlerinde, dosya yapısı çoğunlukla bir ağaca benzer: bir kökü vardır ve başka dallara sahip olabilen dallar vardır, vb. Kök ve dallar dizinlerdir. Örneğin, " С:// " dizini kök olabilir. İki dal içerir: " C://Downloads " ve " C://Users ". Bu şubelerin her birinin iki şubesi vardır: " C://Downloads/Pictures ", " C://Downloads/Video ", " C://Users/JohnSmith ", " C://Users/Pudge2005". Ve bu dalların sırayla başka dalları vb. vardır ve bu nedenle ona ağaç diyoruz. Linux'ta yapı benzer, ancak / dizini kök dizindir. Şimdi kök Dosyalar, Yol - 2dizinden başlamamız gerektiğini hayal edin. , tüm klasörlerini ve alt klasörlerini gözden geçirin ve belirli bir içeriğe sahip dosyaları bulun. "Bu ihtiyacımız olan dosya!" Satırını içeren dosyaları arayacağız Açık olan "testFolder" klasörünü alacağız masaüstü, kök dizin olarak.İçeriği şunlardır: Dosyalar, Yol - 3Level1-a ve level1-b klasörleri ayrıca klasörler içerir: Dosyalar, Yol - 4Dosyalar, Yol - 5Bu "ikinci düzey klasörlerde" klasör yoktur, yalnızca bireysel dosyalar vardır: Dosyalar, Yol - 6Dosyalar, Yol - 7İhtiyacımız olan içeriklere sahip 3 dosyaya kasıtlı olarak açıklayıcı adlar verilmiştir: FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt. Bunlar tam olarak Java kullanarak bulmamız gereken dosyalar. Bunu nasıl yapabiliriz? Bir dosya ağacında gezinmek için çok güçlü bir yöntem yardımımıza koşar: Files.walkFileTree (). İşte yapmamız gerekenler. İlk olarak, bir FileVisitor. FileVisitordosya ağacında gezinme yöntemlerinin açıklandığı özel bir arabirimdir. Özellikle, bir dosyanın içeriğini okumak ve ihtiyacımız olan metni içerip içermediğini kontrol etmek için mantığı koyacağımız yer burasıdır. İşte görünüşümüz 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;
   }
}
Bu durumda, sınıfımız SimpleFileVisitor. FileVisitorBu , yalnızca bir yöntemi geçersiz kılmamız gereken, uygulayan bir sınıftır : visitFile(). Burada her bir dizindeki her dosya ile ne yapılması gerektiğini tanımlıyoruz. Dosya yapısında gezinmek için daha karmaşık bir mantığa ihtiyacınız varsa, kendi uygulamanızı yazmalısınız FileVisitor. Bu sınıfta 3 yöntem daha uygulamanız gerekir:
  • preVisitDirectory(): bir klasöre girmeden önce yürütülecek mantık;

  • visitFileFailed(): bir dosya ziyaret edilemiyorsa (erişim yoksa veya başka nedenlerle) yürütülecek mantık;

  • postVisitDirectory(): bir klasöre girdikten sonra yürütülecek mantık.

Yürütülen böyle bir mantığa ihtiyacımız yok, bu yüzden SimpleFileVisitor. Yöntemin içindeki mantık visitFile()oldukça basittir: dosyadaki tüm satırları okuyun, ihtiyacımız olan içeriği içerip içermediklerini kontrol edin ve öyleyse mutlak yolu konsolda yazdırın. Size zorluk çıkarabilecek tek satır şudur:

return FileVisitResult.CONTINUE;
Aslında bu çok basit. Burada, dosya ziyaret edildikten ve gerekli tüm işlemler yapıldıktan sonra programın yapması gerekenleri basitçe açıklıyoruz. Bizim durumumuzda, ağacı çaprazlamaya devam etmek istiyoruz, bu yüzden seçeneği seçiyoruz CONTINUE. Ancak, alternatif olarak, farklı bir hedefimiz olabilir: "İhtiyacımız olan dosya bu" ifadesini içeren tüm dosyaları bulmak yerine, böyle bir dosya bulun . Bundan sonra, program sonlandırılmalıdır. Bu durumda, kodumuz tamamen aynı görünür, ancak break yerine şöyle olur:

return FileVisitResult.TERMINATE;
Peki, kodumuzu çalıştıralım ve çalışıp çalışmadığına bakalım.

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());
   }
}
Konsol çıktısı:

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
Harika! İşe yaradı! :) Bu küçük zorluğu da kabul edebilirsiniz: SimpleFileVisitorsıradan bir ile değiştirin FileVisitor, 4 yöntemi de geçersiz kılın ve program için kendi amacınızı bulun. Örneğin, tüm eylemlerini günlüğe kaydeden bir program yazabilirsiniz: dosya veya klasörün adını girmeden önce veya sonra görüntüleyin. Şimdilik bu kadar. Yakında görüşürüz! :)
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION