CodeGym /Java Blog /무작위의 /자바 파일, 경로
John Squirrels
레벨 41
San Francisco

자바 파일, 경로

무작위의 그룹에 게시되었습니다
안녕! 오늘은 파일 및 디렉토리 작업에 대해 이야기하겠습니다. 당신은 이미 파일 내용을 관리하는 방법을 알고 있습니다: 우리는 이것에 대해 많은 레슨을 할애했습니다 :) 이러한 목적으로 사용되는 몇 가지 클래스를 쉽게 기억할 수 있을 것 같습니다. 오늘 강의에서는 생성, 이름 변경 등 파일 관리에 대해 구체적으로 설명하겠습니다. Java 7 이전에는 이러한 모든 작업이 File 클래스 를 사용하여 수행되었습니다 . 여기 에서 읽을 수 있습니다 . 그러나 Java 7에서 언어 작성자는 파일 및 디렉토리 작업 방식을 변경하기로 결정했습니다. 이것은 File 클래스에 몇 가지 단점이 있기 때문에 발생했습니다. 예를 들어 파일을 한 위치에서 다른 위치로 복사할 수 있는 copy() 메서드 가 없었습니다 (필수적인 기능처럼 보임). 또한,파일 클래스에는 부울 값을 반환하는 메서드가 상당히 많았습니다 . 오류가 있는 경우 이러한 메서드는 false를 반환합니다. 예외를 발생시키지 않으므로 오류를 식별하고 원인을 진단하기가 매우 어렵습니다. 단일 File 클래스 대신 Paths , PathFiles 의 3개 클래스가 나타났습니다 . 정확히 말하면 Path 는 클래스가 아니라 인터페이스입니다. 그것들이 서로 어떻게 다른지, 그리고 왜 그것들이 각각 필요한지 알아봅시다. 가장 간단한 것부터 시작하겠습니다: 경로 .

경로

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

    콘솔 출력:

    
    false
    true
    

    endsWith () 메서드는 문자 집합이 아닌 실제 경로를 전달해야 합니다. 그렇지 않으면 현재 경로가 실제로 해당 문자 시퀀스로 끝나는 경우에도 결과가 항상 거짓이 됩니다(예: "estFile.txt"의 경우). " 위의 예에서).

    또한 Path에는 절대(전체) 및 상대 경로 작업을 단순화하는 메서드 그룹이 있습니다 .

다음 방법을 살펴보겠습니다.
  • 부울 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" 파일이 포함되어 있음을 의미합니다.

    이봐. "."를 사용하는 경로인 경우 또는 ".."가 프로그램에 표시되면 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 메서드 의 전체 목록은 상당히 깁니다. Oracle 문서 에서 모두 찾을 수 있습니다 . 이제 파일을 살펴보겠습니다 .

파일

Files는 File 클래스 에서 가져온 정적 메서드를 보유하는 유틸리티 클래스입니다. 파일은 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 {

       // 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()파일에 데이터를 쓰는 방법과 데이터를 읽는 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);
       }
   }
}
콘솔 출력:

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"라는 단어로 시작하는 모든 줄을 찾아 대문자로 변환하고 콘솔에 표시합니다. 클래스 를 사용하는 솔루션은 FilesJava 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.
임무는 완수했지만 그렇게 간단한 작업을 위해 우리 코드가 약간... 장황하다고 생각하지 않습니까? 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("As"))
               .map(String::toUpperCase)
               .collect(Collectors.toList());
       result.forEach(System.out::println);
   }
}
우리는 동일한 결과를 달성했지만 훨씬 적은 코드로 달성했습니다! 게다가 아무도 우리가 "가독성"을 잃었다고 말할 수 없습니다. Stream API에 익숙하지 않아도 이 코드의 기능에 대해 쉽게 언급할 수 있다고 생각합니다. 즉, Stream은 다양한 작업을 수행할 수 있는 일련의 요소입니다. 메서드 에서 Stream 개체를 가져온 Files.lines()다음 여기에 3개의 함수를 적용합니다.
  1. filter()파일에서 "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시작해야한다고 상상해보십시오. , 모든 폴더와 하위 폴더를 살펴보고 특정 콘텐츠가 있는 파일을 찾습니다. "This is the file we need!"라는 줄이 포함된 파일을 검색합니다. 다음은 그 내용입니다: 파일, 경로 - 3level1-a 및 level1-b 폴더에도 폴더가 있습니다: 파일, 경로 - 4파일, 경로 - 5이러한 "두 번째 수준 폴더"에는 폴더가 없으며 개별 파일만 있습니다. 파일, 경로 - 6파일, 경로 - 7필요한 내용이 포함된 3개의 파일에는 의도적으로 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. 그러나 다른 목표가 있을 수 있습니다. "이것이 우리에게 필요한 파일입니다."를 포함하는 모든 파일을 찾는 대신 그러한 파일 하나만 찾으십시오 . 그런 다음 프로그램이 종료되어야 합니다. 이 경우 코드는 정확히 동일하게 보이지만 break 대신 다음이 있습니다.

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, 4가지 방법을 모두 재정의하고, 프로그램에 대한 자신의 목적을 생각해 내십시오. 예를 들어 모든 동작을 기록하는 프로그램을 작성할 수 있습니다. 파일이나 폴더를 입력하기 전이나 후에 표시합니다. 지금은 여기까지입니다. 곧 봐요! :)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION