자바의 문자 유형에 대한 심도있는 살펴보기

Java 1.1 버전에는 문자를 처리하기위한 여러 클래스가 도입되었습니다. 이러한 새 클래스는 플랫폼 별 문자 값 개념을 유니 코드 값 으로 변환하기위한 추상화를 만듭니다 . 이 칼럼에서는 추가 된 항목과 이러한 문자 클래스를 추가 한 동기를 살펴 봅니다.

문자 입력

아마도 C 언어에서 가장 많이 남용되는 기본 유형은 char 유형 입니다. 이 8 비트가되도록 정의되어 있기 때문에 타입 일부 남용되고, 지난 25 년 동안 8 비트는 컴퓨터 메모리의 가장 작은 청크를 정의 불가분있다. 후자의 사실과 ASCII 문자 집합이 7 비트에 맞도록 정의되었다는 사실을 결합하면 char 유형은 매우 편리한 "범용"유형을 만듭니다. 또한 C에서 char 유형의 변수에 대한 포인터 는 char 로 참조 될 수있는 모든 것이 캐스팅을 사용하여 다른 유형으로 참조 될 수 있기 때문에 범용 포인터 유형이되었습니다 .

C 언어에서 char 유형 의 사용 및 남용으로 인해 컴파일러 구현간에 많은 비 호환성이 발생했기 때문에 C에 대한 ANSI 표준에서 두 가지 특정 변경이 이루어졌습니다. 범용 포인터가 void 유형을 갖도록 재정의되었으므로 명시 적 프로그래머의 선언; 문자의 숫자 값은 부호가있는 것으로 간주되어 숫자 계산에 사용될 때 처리되는 방법을 정의합니다. 그런 다음 1980 년대 중반 엔지니어와 사용자는 8 비트가 세계의 모든 캐릭터를 표현하기에 충분하지 않다는 것을 알게되었습니다. 불행히도 그 당시에 C는 사람들이 char 의 정의를 변경하기를 꺼려하거나 심지어는 할 수 없을 정도로 확고 해졌습니다.유형. 이제 자바의 초기 시작 인 90 년대로 돌아가 보자. 자바 언어의 설계에서 내려진 많은 원칙 중 하나는 문자가 16 비트라는 것입니다. 이 선택은 다양한 언어로 다양한 종류의 문자를 나타내는 표준 방식 인 Unicode 의 사용을 지원합니다 . 안타깝게도 현재 수정되고있는 다양한 문제에 대한 무대를 마련했습니다.

어쨌든 캐릭터는 무엇입니까?

나는 나 자신이 질문을 발견했을 때 나는 곤경에 알고 있었다 "무슨 일이 그래서 문자?" 글쎄요, 캐릭터는 글자 죠? 한 무리의 글자가 단어를 구성하고 단어가 문장을 형성하는 식입니다. 그러나 현실은 컴퓨터 화면에서 문자를 나타내는 문자 ( 글리프)글리프 ( )를 지정하는 숫자 값 사이의 관계 code point가 전혀 간단하지 않다는 것입니다.

영어 원어민이 된 것은 행운이라고 생각합니다. 첫째, 현대 디지털 컴퓨터의 설계 및 개발에 기여한 많은 사람들의 공통 언어 였기 때문입니다. 둘째, 글리프 수가 비교적 적기 때문입니다. ASCII 정의에는 영어를 쓰는 데 사용할 수있는 96 개의 인쇄 가능한 문자가 있습니다. 20,000 개 이상의 글리프가 정의되어 있고 그 정의가 불완전한 중국어와 비교해보십시오. Morse와 Baudot 코드의 초기부터, 영어의 전반적인 단순성 (문양이 적고 통계적 출현 빈도)은 영어를 디지털 시대의 링구아 프랑카로 만들었습니다. 그러나 디지털 시대에 접어드는 사람들의 수가 증가함에 따라 비 원어민의 수도 증가했습니다. 숫자가 증가함에 따라점점 더 많은 사람들이 컴퓨터가 ASCII를 사용하고 영어 만 사용한다는 사실을 받아들이는 것을 점점 싫어했습니다. 이것은 이해하는 데 필요한 "문자"컴퓨터의 수를 크게 증가 시켰습니다. 그 결과 컴퓨터로 인코딩 된 글리프 수가 두 배로 늘어났습니다.

유효한 7 비트 ASCII 코드가 ISO Latin-1 (또는 ISO 8859_1, "ISO"는 국제 표준기구)이라는 8 비트 문자 인코딩에 통합되었을 때 사용 가능한 문자 수는 두 배가되었습니다. 인코딩 이름으로 수집 한 것처럼이 표준은 유럽 대륙에서 사용되는 많은 라틴어 파생 언어의 표현을 허용했습니다. 그러나 표준이 만들어 졌다고해서 사용할 수 있다는 의미는 아닙니다. 그 당시 많은 컴퓨터는 이미 8 비트 문자로 표현 될 수있는 다른 128 개의 "문자"를 사용하기 시작했습니다. 이러한 추가 문자 사용의 살아남은 두 가지 예는 IBM PC (개인용 컴퓨터)와 가장 인기있는 컴퓨터 터미널 인 Digital Equipment Corporation VT-100입니다.후자는 터미널 에뮬레이터 소프트웨어의 형태로 존재합니다.

8 비트 문자의 실제 사망 시간은 의심 할 여지없이 수십 년 동안 논쟁의 여지가 있지만 1984 년 매킨토시 컴퓨터가 도입 될 때이를 고수했습니다. 매킨토시는 주류 컴퓨팅에 매우 혁신적인 두 가지 개념을 도입했습니다. 램; 모든 언어의 문자를 나타내는 데 사용할 수있는 WorldScript. 물론 이것은 단순히 Xerox가 Star 워드 프로세싱 시스템의 형태로 Dandelion 클래스 시스템에 배송 해 온 것의 사본 일 뿐이지 만 Macintosh는 이러한 새로운 문자 세트와 글꼴을 여전히 "멍청한"터미널을 사용하는 청중에게 가져 왔습니다. . 일단 시작된 후에는 다른 글꼴의 사용을 중단 할 수 없었습니다. 너무 많은 사람들에게 너무 매력적이었습니다. 80 년대 후반까지이러한 모든 문자의 사용을 표준화해야한다는 압력은 1990 년에 첫 번째 사양을 발표 한 유니 코드 컨소시엄의 형성과 함께 대두되었습니다. 안타깝게도 80 년대와 심지어 90 년대에도 문자 집합의 수가 배가되었습니다. 당시 새로운 문자 코드를 생성 한 엔지니어 중 극소수 만이 초기 유니 코드 표준을 실행 가능하다고 생각했기 때문에 글리프에 대한 자체 코드 매핑을 만들었습니다. 따라서 유니 코드는 잘 받아 들여지지 않았지만 128 자 또는 최대 256 자만 사용할 수 있다는 생각은 확실히 사라졌습니다. 매킨토시 이후, 다양한 글꼴에 대한 지원은 워드 프로세싱의 필수 기능이되었습니다. 8 비트 캐릭터가 사라져 가고있었습니다.80 년대부터 90 년대까지도 문자 집합의 수가 증가했습니다. 당시 새로운 문자 코드를 생성 한 엔지니어 중 극소수 만이 초기 유니 코드 표준을 실행 가능하다고 생각했기 때문에 글리프에 대한 자체 코드 매핑을 만들었습니다. 따라서 유니 코드는 잘 받아 들여지지 않았지만 128 자 또는 최대 256 자만 사용할 수 있다는 생각은 확실히 사라졌습니다. 매킨토시 이후, 다양한 글꼴에 대한 지원은 워드 프로세싱의 필수 기능이되었습니다. 8 비트 캐릭터가 사라져 가고있었습니다.80 년대부터 90 년대까지도 문자 집합의 수가 증가했습니다. 당시 새로운 문자 코드를 생성 한 엔지니어 중 극소수 만이 초기 유니 코드 표준을 실행 가능하다고 생각했기 때문에 글리프에 대한 자체 코드 매핑을 만들었습니다. 따라서 유니 코드는 잘 받아 들여지지 않았지만 128 자 또는 최대 256 자만 사용할 수 있다는 생각은 확실히 사라졌습니다. 매킨토시 이후, 다양한 글꼴에 대한 지원은 워드 프로세싱의 필수 기능이되었습니다. 8 비트 캐릭터가 사라져 가고있었습니다.사용 가능한 문자가 128 자 또는 최대 256 자라는 개념은 확실히 사라졌습니다. 매킨토시 이후, 다양한 글꼴에 대한 지원은 워드 프로세싱의 필수 기능이되었습니다. 8 비트 캐릭터가 사라져 가고있었습니다.사용 가능한 문자가 128 자 또는 최대 256 자라는 개념은 확실히 사라졌습니다. 매킨토시 이후, 다양한 글꼴에 대한 지원은 워드 프로세싱의 필수 기능이되었습니다. 8 비트 캐릭터가 사라져 가고있었습니다.

자바와 유니 코드

1992 년 Sun에서 Oak 그룹 (Java 언어는 처음 개발되었을 때 Oak라고 불림)에 합류했을 때 이야기에 들어갔습니다. 기본 유형 charJava에서 유일한 unsigned 유형 인 16 개의 unsigned bits로 정의되었습니다. 16 비트 문자의 근거는 모든 유니 코드 문자 표현을 지원하므로 Java가 유니 코드가 지원하는 모든 언어로 문자열을 표현하는 데 적합하다는 것입니다. 그러나 문자열을 표현할 수 있고 인쇄 할 수 있다는 것은 항상 별개의 문제였습니다. Oak 그룹의 대부분의 경험이 Unix 시스템과 Unix 파생 시스템에서 나왔음을 감안할 때 가장 편안한 문자 집합은 다시 ISO Latin-1이었습니다. 또한 그룹의 Unix 유산을 통해 Java I / O 시스템은 대부분의 Unix 스트림 추상화에서 모델링되어 모든 I / O 장치가 8 비트 바이트 스트림으로 표현 될 수 있습니다. 이 조합은 8 비트 입력 장치와 Java의 16 비트 문자 사이의 언어에 잘못된 기능을 남겼습니다. 그러므로,8 비트 스트림에서 Java 문자열을 읽거나 써야하는 곳이라면 어디에서나 8 비트 문자를 16 비트 유니 코드로 마술처럼 매핑하는 약간의 코드, 해킹이있었습니다.

JDK (Java Developer Kit) 1.0 버전에서 입력 해킹은 DataInputStream클래스에 있었고 출력 해킹은 전체 PrintStream클래스였습니다. (실제로 TextInputStreamJava의 alpha 2 릴리스에 이름이 지정된 입력 클래스가 있었지만 DataInputStream실제 릴리스 에서는 해킹 으로 대체 되었습니다.) 이것은 C에 해당하는 Java를 필사적으로 검색하기 때문에 초보 Java 프로그래머에게 계속 문제를 일으 킵니다. 기능 getc(). 다음 Java 1.0 프로그램을 고려하십시오.

import java.io. *; 공개 클래스 가짜 {공개 정적 void main (String args []) {FileInputStream fis; DataInputStream dis; char c; 시도 {fis = new FileInputStream ( "data.txt"); dis = new DataInputStream (fis); while (true) {c = dis.readChar (); System.out.print (c); System.out.flush (); if (c == '\ n') break; } fis.close (); } catch (예외 e) {} System.exit (0); }}

언뜻보기에이 프로그램은 파일을 열고 한 번에 한 문자 씩 읽고 첫 번째 줄 바꿈을 읽으면 종료하는 것처럼 보입니다. 그러나 실제로 얻는 것은 정크 출력입니다. 그리고 당신이 쓰레기를 얻는 이유는 readChar 가 16 비트 유니 코드 문자를 읽고 System.out.print그것이 ISO Latin-1 8 비트 문자라고 가정하는 것을 출력하기 때문입니다. 당신이 사용하는 위의 프로그램 변경하는 경우에는, 의 readline 의 기능을 DataInputStream,이 작품에 나타납니다 때문에의 코드 내의 readLine유니 코드 사양에 "수정 된 UTF-8"로 전달되는 nod로 정의 된 형식을 읽습니다. (UTF-8은 8 비트 입력 스트림에서 유니 코드 문자를 나타 내기 위해 유니 코드가 지정하는 형식입니다.) 따라서 Java 1.0의 상황은 Java 문자열이 16 비트 유니 코드 문자로 구성되지만 매핑되는 매핑은 하나뿐입니다. ISO Latin-1 문자를 유니 코드로 변환합니다. 다행히 유니 코드는 코드 페이지 "0"(즉, 상위 8 비트가 모두 0 인 256 자)을 ISO Latin-1 집합과 정확히 일치하도록 정의합니다. 따라서 매핑은 매우 사소하며 ISO Latin-1 문자 파일 만 사용하는 한 데이터가 파일을 떠나고 Java 클래스에 의해 조작 된 다음 파일에 다시 작성 될 때 문제가 발생하지 않습니다. .

There were two problems with burying the input conversion code into these classes: Not all platforms stored their multilingual files in modified UTF-8 format; and certainly, the applications on these platforms didn't necessarily expect non-Latin characters in this form. Therefore, the implementation support was incomplete, and there was no easy way to add the needed support in a later release.

Java 1.1 and Unicode

The Java 1.1 release introduced an entirely new set of interfaces for handling characters, called Readers and Writers. I modified the class named bogus from above into a class named cool. The cool class uses an InputStreamReader class to process the file rather than the DataInputStream class. Note that InputStreamReader is a subclass of the new Reader class and the System.out is now a PrintWriter object, which is a subclass of the Writer class. The code for this example is shown below:

import java.io.*; public class cool { public static void main(String args[]) { FileInputStream fis; InputStreamReader irs; char c; try { fis = new FileInputStream("data.txt"); irs = new InputStreamReader(fis); System.out.println("Using encoding : "+irs.getEncoding()); while (true) { c = (char) irs.read(); System.out.print(c); System.out.flush(); if (c == '\n') break; } fis.close(); } catch (Exception e) { } System.exit(0); } } 

The primary difference between this example and the previous code listing is the use of the InputStreamReader class rather than the DataInputStream class. Another way in which this example is different from the previous one is that there is an additional line that prints out the encoding used by the InputStreamReader class.

The important point is that the existing code, once undocumented (and ostensibly unknowable) and embedded inside the implementation of the getChar method of the DataInputStream class, has been removed (actually its use is deprecated; it will be removed in a future release). In the 1.1 version of Java, the mechanism that performs the conversion is now encapsulated in the Reader class. This encapsulation provides a way for the Java class libraries to support many different external representations of non-Latin characters while always using Unicode internally.

Of course, like the original I/O subsystem design, there are symmetric counterparts to the reading classes that perform writing. The class OutputStreamWriter can be used to write strings to an output stream, the class BufferedWriter adds a layer of buffering, and so on.

Trading warts or real progress?

The somewhat lofty goal of the design of the Reader and Writerclasses was to tame what is currently a hodge-podge of representation standards for the same information by providing a standard way of converting back and forth between the legacy representation -- be it Macintosh Greek or Windows Cyrillic -- and Unicode. So, a Java class that deals with strings need not change when it moves from platform to platform. This might be the end of the story, except that now that the conversion code is encapsulated, the question arises as to what that code assumes.

이 칼럼을 조사하는 동안 나는 비서가 카본 페이퍼를 넣는 것이 상당히 쉬웠 기 때문에 복사기가 불필요하다는 Xerox 경영진 (이전의 Xerox, Haloid Company)의 유명한 인용문을 떠올 렸습니다. 타자기와 원본을 만드는 동안 문서의 사본을 만듭니다. 물론 뒤에서 볼 때 분명한 것은 복사기가 문서를 생성하는 사람보다 문서를받는 사람에게 훨씬 더 많은 혜택을 준다는 것입니다. JavaSoft는 시스템의이 부분을 설계 할 때 문자 인코딩 및 디코딩 클래스를 사용하는 것과 유사한 통찰력이 부족함을 보여주었습니다.