JEP 400: UTF-8 by Default

JEP 400: UTF-8 by Default

Summary

표준 Java API의 기본 문자 집합으로 UTF-8을 지정합니다.
이 변경으로 기본 문자 집합에 의존하는 API는 모든 구현, 운영 체제, 로케일 및 구성에서 일관되게 작동합니다.

Goals

  1. 코드가 기본 문자 집합에 의존할 때 Java 프로그램을 보다 예측 가능하고 이식 가능하게 만듭니다.
  2. 표준 Java API가 기본 문자 집합을 사용하는 위치를 명확히 합니다.
  3. 콘솔 I/O를 제외한 표준 Java API 전체에서 UTF-8로 표준화합니다.

Non-Goals

  1. 새로운 표준 Java API 또는 지원되는 JDK API를 정의하는 것이 목표는 아니지만, 이러한 노력은 새로운 편리한 방법이 기존 API에 더 접근하기 쉽고 사용하기 쉽게 만들 수 있는 기회를 식별할 수 있습니다.
  2. 명시적 charset 매개변수를 사용하는 대신 기본 charset에 의존하는 표준 Java API를 더 이상 사용하지 않거나 제거하려는 의도는 없습니다.

Motivation

파일 읽기 및 쓰기, 텍스트 처리를 위한 표준 Java API를 사용하면 문자 집합 을 인수로 전달할 수 있습니다.
char문자 집합은 원시 바이트와 Java 프로그래밍 언어의 16비트 값 간의 변환을 제어합니다.
지원되는 문자 집합에는 예를 들어 US-ASCII, UTF-8 및 ISO-8859-1이 포함됩니다.

charset 인수가 전달되지 않으면 표준 Java API는 일반적으로 기본 charset 을 사용합니다.
JDK는 운영 체제, 사용자 로케일 및 기타 요소와 같은 런타임 환경을 기반으로 시작 시 기본 문자 집합을 선택합니다.

기본 charset은 모든 곳에서 동일하지 않기 때문에 기본 charset을 사용하는 API는 숙련된 개발자에게도 분명하지 않은 많은 위험을 초래합니다.

java.io.FileWriter문자 집합을 전달하지 않고 생성한 다음 이를 사용하여 일부 텍스트를 파일에 쓰는 응용 프로그램을 고려 하십시오.
결과 파일에는 응용 프로그램을 실행하는 JDK의 기본 문자 집합을 사용하여 인코딩된 바이트 시퀀스가 포함됩니다.
다른 시스템에서 실행되거나 동일한 시스템에서 다른 사용자가 실행하는 두 번째 응용 프로그램 java.io.FileReader은 문자 집합을 전달하지 않고 을 만들고 이를 사용하여 해당 파일의 바이트를 읽습니다.
결과 텍스트에는 두 번째 응용 프로그램을 실행하는 JDK의 기본 문자 집합을 사용하여 디코딩된 일련의 문자가 포함됩니다.
기본 charset이 첫 번째 응용 프로그램의 JDK와 두 번째 응용 프로그램의 JDK 간에 다르면 결과 텍스트가 자동으로 손상되거나 불완전할 수 있습니다.
다음은 macOS 에서 UTF-8 인코딩된 일본어 텍스트 파일을 미국 영어 또는 일본어 로케일로 Windows에서 읽을 때 손상되는 이 위험의 예입니다 .

java.io.FileReader(“hello.txt”) -> “こんにちは” (macOS)
java.io.FileReader(“hello.txt”) -> “ã?“ã‚“ã?«ã?¡ã? ” (Windows (en-US))
java.io.FileReader(“hello.txt”) -> “縺ォ縺。縺ッ” (Windows (ja-JP)

이러한 위험에 익숙한 개발자는 charset 인수를 명시적으로 취하는 메서드와 생성자를 사용할 수 있습니다.
그러나 인수를 전달해야 하는 경우 스트림 파이프라인에서 메서드 및 생성자가 메서드 참조(::)를 통해 사용되는 것을 방지할 수 있습니다.

file.encoding개발자는 때때로 명령줄에서 시스템 속성을 설정하여 기본 문자 집합을 구성하려고 시도 하지만(예: java -Dfile.encoding=…), 이것은 지원되지 않았습니다.
또한 Java 런타임이 시작된 후 프로그래밍 방식으로(예: System.setProperty(…)) 속성을 설정하려고 시도하면 작동하지 않습니다.

모든 표준 Java API가 JDK의 기본 문자 집합 선택을 따르는 것은 아닙니다.
예를 들어 인수 java.nio.file.Files없이 파일을 읽거나 쓰는 메서드는 Charset항상 UTF-8을 사용하도록 지정됩니다.
최신 API는 기본적으로 UTF-8을 사용하는 반면 이전 API는 기본 charset을 사용하도록 기본 설정되어 있다는 사실은 API를 혼합하여 사용하는 애플리케이션에 위험합니다.

기본 charset이 모든 곳에서 동일하도록 지정되면 전체 Java 에코시스템에 이점이 있습니다.
이식성을 고려하지 않는 응용 프로그램은 거의 영향을 받지 않는 반면 charset 인수를 전달하여 이식성을 수용하는 응용 프로그램은 영향을 받지 않습니다.
UTF-8은 오랫동안 World Wide Web에서 가장 일반적인 문자 집합 이었습니다.
UTF-8은 방대한 수의 Java 프로그램에서 처리되는 XML 및 JSON 파일의 표준이며 Java 자체 API는 예를 들어 NIO API 및 속성 파일 에서 점점 더 UTF-8을 선호합니다.
따라서 모든 Java API에 대한 기본 문자 집합으로 UTF-8을 지정하는 것이 좋습니다.

우리는 이 변경이 JDK 18로 마이그레이션하는 프로그램에 광범위한 호환성 영향을 미칠 수 있음을 알고 있습니다.
이러한 이유로 기본 문자 집합이 환경에 따라 달라지는 JDK 18 이전 동작을 항상 복구할 수 있습니다.

Description

JDK 17 및 이전 버전에서 기본 문자 집합은 Java 런타임이 시작될 때 결정됩니다.
macOS에서는 POSIX C 로케일을 제외하고는 UTF-8입니다.
다른 운영 체제에서는 사용자의 로케일과 기본 인코딩에 따라 다릅니다.
예를 들어 Windows에서는 windows-1252또는 와 같은 코드 페이지 기반 문자 집합 windows-31j입니다.
이 메서드 java.nio.charsets.Charset.defaultCharset()는 기본 문자 집합을 반환합니다.
현재 JDK의 기본 문자 집합을 확인하는 빠른 방법은 다음 명령을 사용하는 것입니다.

1
java -XshowSettings:properties -version 2>&1 | grep file.encoding

다음을 포함하여 여러 표준 Java API가 기본 문자 집합을 사용합니다.

  • java.io package, InputStreamReader, FileReader, OutputStreamWriter, FileWriter, 그리고 PrintStream 에서 기본 문자 집합 을 사용하여 인코딩하거나 디코딩하는 판독기 작성기 및 인쇄 스트림을 만드는 생성자를 정의합니다.
  • java.util package, Formatter 그리고 Scanner 기본 문자 집합을 사용하는 생성자를 정의합니다
  • java.net package, URLEncoder 그리고 URLDecoder 에서 기본 문자 집합을 사용하는 메서드를 정의합니다

구현별 수단으로 달리 구성하지 않는 한 Charset.defaultCharset()기본 charset이 UTF-8 이라고 의 사양을 변경할 것을 제안합니다.
(JDK를 구성하는 방법은 아래를 참조하십시오.) UTF-8 문자 집합은 RFC 2279 에 의해 지정됩니다.
기반이 되는 변환 형식은 ISO 10646-1의 수정 2에 지정되어 있으며 유니코드 표준 에도 설명되어 있습니다.
Modified UTF-8 과 혼동하지 마십시오 .

기본 charset을 사용하여 상호 참조하는 모든 표준 Java API의 사양을 업데이트합니다 Charset.defaultCharset().
이러한 API에는 위에 나열된 API가 포함되지만 에서 지정한 대로 문자 집합이 지정되는 System.out및 는 포함되지 않습니다.
System.errConsole.charset()

file.encoding속성 및 native.encoding

Charset.defaultCharset() 의 사양에서 예상한 대로 JDK는 기본 문자 집합을 UTF-8이 아닌 다른 것으로 구성하도록 허용합니다.
file.encoding 명령줄에서 설정하는 것이 기본 문자 집합을 구성하는 데 지원되는 수단이 되도록 시스템 속성의 처리를 수정할 것 입니다.
우리는 다음과 같은 구현 노트에 이것을 명시할 것입니다 System.getProperties():

file.encoding (즉, “java -Dfile.encoding=COMPAT) “COMPAT 로 설정된 경우 기본 문자 집합은 사용자의 운영 체제,
로케일 및 기타 요소를 기반으로 JDK 17 및 이전 버전의 알고리즘에서 선택한 문자 집합이 됩니다.
file.encoding 의 값은 해당 문자 집합의 이름으로 설정됩니다.

file.encoding가 “UTF-8”(즉, )로 설정 되면 java -Dfile.encoding=UTF-8기본 문자 집합은 UTF-8이 됩니다.
이 no-op 값은 기존 명령줄의 동작을 유지하기 위해 정의됩니다.

java -Dfile.encoding=UTF-8 …UTF-8이 기본 문자 집합인 JDK에 배포하기 전에 개발자는 현재 JDK(8-17) 에서 Java 런타임을 시작하여 문자 집합 문제를 확인하는 것이 좋습니다 .

JDK 17 native.encoding은 기본 문자 집합이 실제로 해당 문자 집합으로 구성되었는지 여부에 관계없이 프로그램이 JDK 알고리즘에 의해 선택된 문자 집합을 얻는 표준 방법으로 시스템 속성을 도입했습니다.
JDK 18에서 가 명령줄에서 로 file.encoding설정된 경우 의 런타임 값은 의 런타임 값과 동일합니다.

아래 의 위험 및 가정 에서는 이 변경으로 인해 발생할 수 있는 비호환성을 완화하는 방법 과 응용 프로그램에 file.encoding대한 native.encoding시스템 속성 및 권장 사항에 대해 설명합니다.

JDK에서 내부적으로 사용하는 세 가지 charset 관련 시스템 속성이 있습니다. 이들은 지정되지 않고 지원되지 않지만 완전성을 위해 여기에 문서화되어 있습니다.

sun.stdout.encoding및 - 표준 출력 스트림( ) 및 표준 오류 스트림( ) 및 API 에 sun.stderr.encoding사용되는 문자 집합의 이름 .System.outSystem.errjava.io.Console

sun.jnu.encodingjava.nio.file— 파일 내용과 달리 파일 이름 경로를 인코딩 또는 디코딩할 때 구현에 사용되는 문자 집합의 이름입니다 . macOS에서 그 값은 “UTF-8”; 다른 플랫폼에서는 일반적으로 기본 charset입니다.

Java 언어를 사용하면 소스 코드에서 UTF-16 인코딩으로 유니코드 문자를 표현할 수 있으며 , 이는 기본 문자 집합에 대해 UTF-8을 선택해도 영향을 받지 않습니다.
그러나 컴파일러는 옵션 에 의해 다르게 구성되지 않는 한 소스 파일이 기본 charset으로 인코딩 javac된다고 가정하기 때문에 영향을 받습니다 .
소스 파일이 UTF-8이 아닌 인코딩으로 저장되고 이전 JDK로 컴파일된 경우 JDK 18 이상에서 다시 컴파일하면 문제가 발생할 수 있습니다.
예를 들어, UTF-8이 아닌 소스 파일에 ASCII가 아닌 문자가 포함된 문자열 리터럴이 있는 경우 가 사용 되지 않는 한 해당 리터럴은 JDK 18 이상에서 잘못 해석될 수 있습니다 ..java-encoding javac-encoding

Testing

이 변경이 호환성에 미치는 영향의 범위를 이해하려면 상당한 테스트가 필요합니다. 지리적으로 다양한 사용자 인구를 가진 개발자 또는 조직의 테스트가 필요합니다.

-Dfile.encoding=UTF-8개발자는 이 변경 사항이 적용된 초기 액세스 또는 GA 릴리스보다 먼저 실행하여 기존 JDK 릴리스의 문제를 확인할 수 있습니다.

Risks and Assumptions

우리는 많은 환경의 애플리케이션이 Java의 선택에 영향을 받지 않는다고 가정합니다

macOS에서 기본 문자 집합은 POSIX C 로케일을 사용하도록 구성된 경우를 제외하고 여러 릴리스에서 UTF-8이었습니다.

많은 Linux 배포판에서 전부는 아니지만 기본 문자 집합은 UTF-8이므로 이러한 환경에서는 변경 사항을 식별할 수 없습니다.

많은 서버 응용 프로그램이 이미 로 시작 -Dfile.encoding=UTF-8되었으므로 변경 사항이 발생하지 않습니다.

Alternatives

상 유지 — 이것은 위에서 설명한 위험을 제거하지 않습니다.

기본 charset을 사용하는 Java API의 모든 메서드 사용 중단 — 개발자가 charset 매개 변수를 사용하는 생성자와 메서드를 사용하도록 권장하지만 결과 코드는 더 장황해집니다.

변경할 수단을 제공하지 않고 UTF-8을 기본 문자 집합으로 지정 — 이 변경으로 인한 호환성 영향이 너무 큽니다.

참조