1. 개요
이 사용방법(예제)에서는 Java의 두 절대 경로에서 상대 경로를 구성하는 방법을 배웁니다. 새로운 I/O(NIO2) 경로 API와 URI 클래스라는 두 가지 기본 제공 Java API에 중점을 둘 것입니다.
2. 절대 경로와 상대 경로
시작하기 전에 간단히 요약해 보겠습니다. 텍스트의 모든 예제에 대해 사용자의 홈 디렉토리에서 동일한 파일 구조를 사용합니다.
/ (root)
|-- baeldung
\-- bar
| |-- one.txt
| |-- two.txt
\-- foo
|-- three.txt
절대 경로는 루트 노드에서 시작하여 현재 작업 디렉터리에 관계없이 위치를 설명합니다. 파일의 절대 경로는 다음과 같습니다.
one.txt -> /baeldung/bar/one.txt
two.txt -> /baeldung/bar/two.txt
three.txt -> /baeldung/foo/three.txt
작업 디렉토리를 변경하더라도 절대 경로는 항상 동일하게 유지됩니다.
반면에 상대 경로는 해당 소스에 상대적인 대상 노드의 위치를 설명합니다 . 현재 baeldung 디렉토리에 있는 경우 파일의 상대 경로를 살펴보겠습니다.
one.txt -> ./bar/one.txt
two.txt -> ./bar/two.txt
three.txt -> ./foo/three.txt
이제 bar 하위 디렉토리로 이동하여 상대 경로를 다시 확인합니다.
one.txt -> ./one.txt
two.txt -> ./two.txt
three.txt -> ../foo/three.txt
보시다시피 결과는 약간 다릅니다. 절대 경로는 상수인 반면 소스 컨텍스트를 수정하면 상대 값이 변경될 수 있음을 기억해야 합니다 . 절대 경로는 소스 노드가 시스템의 루트인 상대 경로의 특수한 경우입니다.
3. NIO2 API
이제 상대 경로와 절대 경로가 어떻게 작동하는지 알았으므로 NIO2 API를 확인해야 합니다 . 아시다시피 NIO2 API는 Java 7 릴리스와 함께 도입되었으며 많은 함정이 있던 이전 I/O API를 개선했습니다 . 이 API를 사용하여 절대 경로로 설명된 두 파일 간의 상대 경로를 결정하려고 합니다.
파일에 대한 Path 개체를 구성하여 시작하겠습니다 .
Path pathOne = Paths.get("/baeldung/bar/one.txt");
Path pathTwo = Paths.get("/baeldung/bar/two.txt");
Path pathThree = Paths.get("/baeldung/foo/three.txt");
소스와 주어진 노드 사이의 상대 경로를 구성하려면 Path 클래스 에서 제공하는 relativize(Path) 메서드 를 사용할 수 있습니다.
Path result = pathOne.relativize(pathTwo);
assertThat(result)
.isRelative()
.isEqualTo(Paths.get("../two.txt"));
보시다시피 결과는 확실히 상대 경로입니다. 그 맞습니까? 특히 처음에 부모 연산자( ../ )가 있습니까?
상대 경로는 디렉토리 또는 파일이 될 수 있는 모든 유형의 노드에서 시작하여 지정될 수 있음을 기억해야 합니다. 특히 CLI나 탐색기를 사용할 때 디렉토리 작업을 합니다. 그런 다음 모든 상대 경로는 현재 작업 디렉토리를 기반으로 계산됩니다.
이 예에서는 특정 파일을 가리키는 경로 를 만들었습니다 . 그래서 우리는 먼저 파일의 부모 디렉토리로 이동한 다음 두 번째 파일로 이동해야 합니다. 전반적으로 결과는 정확합니다.
결과를 소스 디렉토리에 상대적으로 만들려면 getParent() 메서드를 사용할 수 있습니다.
Path result = pathOne.getParent().relativize(pathTwo);
assertThat(result)
.isRelative()
.isEqualTo(Paths.get("two.txt"));
Path 개체 는 모든 파일이나 디렉터리를 가리킬 수 있습니다. 더 복잡한 논리를 구축하는 경우 추가 검사를 제공해야 합니다.
마지막으로 one.txt 파일 과 three.txt 파일 간의 상대 경로를 확인 합니다.
Path resultOneToThree = pathOne.relativize(pathThree);
Path resultThreeToOne = pathThree.relativize(pathOne);
assertThat(resultOneToThree)
.isRelative()
.isEqualTo(Paths.get("..\..\foo\three.txt"));
assertThat(result)
.isRelative()
.isEqualTo(Paths.get("..\..\bar\one.txt"));
이 빠른 테스트 는 상대 경로가 상황에 따라 다르다는 것을 확인합니다 . 절대 경로는 여전히 동일하지만 소스 노드와 대상 노드를 함께 교환하면 상대 경로가 달라집니다.
4. java.net.URI API
NIO2 API를 확인한 후 java.net.URI 클래스로 이동합니다. URI (Uniform Resource Identifier)는 파일 작업 시 사용할 수 있는 모든 리소스를 식별할 수 있는 문자열이라는 것을 알고 있습니다.
파일에 대한 URI 개체를 구성해 보겠습니다 .
URI uriOne = pathOne.toURI();
// URI uriOne = URI.create("file:///baeldung/bar/one.txt")
URI uriTwo = pathTwo.toURI();
URI uriThree = pathThree.toURI();
String 을 사용하여 URI 객체를 구성 하거나 이전에 만든 Path 를 변환할 수 있습니다 .
이전과 마찬가지로 URI 클래스는 relativize(URI) 메서드도 제공합니다. 이를 사용하여 상대 경로를 구성해 보겠습니다.
URI result = uriOne.relativize(uriTwo);
assertThat(result)
.asString()
.contains("file:///baeldung/bar/two.txt");
결과는 우리가 예상한 것과 다르며 상대 경로가 올바르게 구성되지 않았습니다. 이것이 왜 그런지에 대한 질문에 대답하려면 클래스의 공식 문서 를 확인해야 합니다 .
이 메서드는 원본 URI가 대상 URI의 접두사인 경우에만 상대 값을 반환합니다. 그렇지 않으면 대상 값을 반환합니다. 결과적으로 파일 노드 간에 상대 경로를 구축할 수 없습니다. 이 시나리오에서는 하나의 URI가 다른 URI의 접두사로 사용되지 않습니다.
상대 경로를 반환하기 위해 소스 URI 를 첫 번째 파일의 디렉터리로 설정할 수 있습니다 .
URI uriOneParent = pathOne.getParent().toUri(); // file:///baeldung/bar/
URI result = uriOneParent.relativize(uriTwo);
assertThat(result)
.asString()
.contains("two.txt");
이제 소스 노드가 대상 접두사이므로 결과가 올바르게 계산됩니다. 방법의 제한으로 인해 URI 방법 을 사용하여 one.txt/two.txt 및 three.txt 파일 간의 상대 경로를 확인할 수 없습니다. 해당 디렉토리에는 공통 접두어가 없습니다.
5. 요약
이 기사에서는 절대 경로와 상대 경로의 주요 차이점을 살펴보는 것으로 시작했습니다.
다음으로 절대 경로로 설명된 두 파일 사이에 상대 경로를 구성했습니다. NIO2 API를 확인하는 것으로 시작하여 상대 경로 구축 프로세스를 자세히 설명했습니다.
마지막으로 java.net.URI 클래스 로 동일한 결과를 얻으려고 했습니다 . 제한 사항으로 인해 이 API를 사용하여 모든 변환을 수행할 수 없다는 것을 알았습니다.
항상 그렇듯이 추가 테스트가 포함된 모든 예제는 GitHub 에서 찾을 수 있습니다 .