본문 바로가기

Study/Java

[Java] 정규표현식을 이용한 파일구분자 바꾸기

반응형

프로그램 실행에 필요한 설정 정보를 코드가 아닌 외부 파일에서 읽어오는 경우가 있다.

예를 들어 영상 편집프로그램을 만든다고 할 경우 영상 배경음을 불러오는 기능이 필요하다.

 

파일 경로

 

파일의 경로를 설정하는 방법은 두 가지가 있다.

절대 경로 : 최상위 디렉터리(루트 디렉터리)부터 목표지점 까지의 경로를 나타내는 방법

상대 경로 : 특정 지점부터 목표지점까지의 경로를 나타내는 방법

 

서울시청을 가야하는 상황을 생각해보자.

절대 경로는 '서울 중구 세종대로 110'이다. 내가 어디에 있는지에 상관없이 저 주소를 향해 걸어가면 서울시청에 갈 수 있다.

 

상대경로는 내가 지금 있는 곳에서 목표지점까지의 상대적인 경로를 나타낸 것이다. 내가 지금 지하철을 타고 시청역에 도착한 상태다. 여기서 서울 시청을 가려면 4번출구를 향해서 100미터를 걸어간 뒤 계단을 걸어 지상으로 이동한다. 그리고 오른쪽으로 방향을 바꾸고 50미터를 걸어가야 한다.

 

사용자가 프로그램 언어에 대한 이해가 있어서 문자열 이스케이프 처리를 해주면 좋겠지만 그렇지 않은 경우가 대부분이다. 이런 예외 상황을 위해서 사용자가 입력할 수 있는 예상값 목록을 정리해두고 이 값들을 올바른 값으로 바꿔주면 프로그램 실행에 문제가 없게 된다.

 

예제 코드는 '/' 또는 '\(또는 원화기호 \)'를 Java 프로그램이 실행중인 환경의 파일경로 구분자로 바꾸는 코드다.

File.separator는 오라클 자바 문서에 나오는 것처럼 사용환경에 의존적인 파일과 디렉터리 이름을 구분하기 위한 '경로이름 문자열'이다.
User interfaces and operating systems use system-dependent pathname strings to name files and directories

리눅스 환경에서는 '/'을 사용하고, 윈도우 환경에서는 '\\\\(\\이지만 \은 이스케이프 문자이므로 각각 한번씩 더 적어줌)'으로 표현된다.

 

public static void main(String[] args) {

  // '/' 또는 '\'가 있는지 확인하는 정규표현식
  String regex = "[\\\\/]";
  boolean case1 = "/".matches(regex);
  System.out.println("/ = " + case1);

  boolean case2 = "\\".matches(regex);
  System.out.println("/ = " + case2);

  // 프로그램이 실행되는 환경에 따라 JVM이 파일 경로 구분자를 정해준다
  // File.separator를 사용하여 파일 경로 구분자로 바꾼다
  String replacedText = Matcher.quoteReplacement(File.separator);
  String replace1 = "./".replaceAll(regex, replacedText);
  System.out.println("./ replace --> " + replace1);

  String replace2 = ".\\".replaceAll(regex, replacedText);
  System.out.println(".\\ replace --> " + replace2);

  String replace3 = "./test/log".replaceAll(regex, replacedText);
  System.out.println("./test/log replace --> " + replace3);

  String replace4 = ".\\test\\log".replaceAll(regex, replacedText);
  System.out.println(".\\test\\log replace --> " + replace4);

  String replace5 = "./test\\log".replaceAll(regex, replacedText);
  System.out.println("./test\\log replace --> " + replace5);

  String replace6 = ".\\test/log".replaceAll(regex, replacedText);
  System.out.println(".\\test/log replace --> " + replace6);
  }
}

 

위의 예제코드를 실행하면 아래와 같이 결과를 확인할 수 있다.

자바 정규표현식은 여기에서 실행해볼 수 있다.

 

소스코드 중 'Matcher.quoteReplacement(File.separator)'를 사용한 부분이 눈에 띈다. Matcher.quoteReplacement() 메소드를 사용하지 않고 "./".replaceAll(regex, File.separator)처럼 직접 문자열을 변환하려고 할 경우 에러가 발생한다.

Exception in thread "main" java.lang.IllegalArgumentException: character to be escaped is missing
  at java.util.regex.Matcher.appendReplacement(Unknown Source)
  at java.util.regex.Matcher.replaceAll(Unknown Source)
  at java.lang.String.replaceAll(Unknown Source)
  at RegexCheck.main(RegexCheck.java:7)

 

에러 참고 링크 : bugs.openjdk.java.net/browse/JDK-8165200

윈도우 환경에서는 파일경로 구분자가 '\'이기 때문이다. 앞에서도 설명했듯이 '\'는 이스케이프 문자열이어서 '\'를 출력, 사용하려면 '\\'처럼 이스케이핑처리를 해주어야한다. Matcher.quoteReplacement()는 매개변수로 입력한 값을 아무런 의미가 없게 만들어준다. 즉 '\'을 있는 그대로 사용할 수 있게 해주는 것이다.

 

오라클의 문서를 참고해보면 자세히 설명되어 있다.

Returns a literal replacement String for the specified String.
특정 문자열을 위한 리터럴 대체 문자열을 반환한다.

This method produces a String that will work as a literal replacement s in the appendReplacement method of the Matcher class.
이 메소드는 Matcher 클래스의 appendReplacement 메소드를 통해 리터럴 대체 문자열로서 동작하는 문자열을 만든다.

The String produced will match the sequence of characters in s treated as a literal sequence.
생성된 대상 문자열은 리터럴 연속값으로 취급되는 문자들과 일치한다.

Slashes ('\') and dollar signs ('$') will be given no special meaning.
슬래시('\'), 달러 기호('$')는 특별한 의미를 갖지 않게 된다.

 

반응형