문자 세트 기본 사항:
문자 세트
문자 모음, 즉 특별한 의미를 지닌 기호입니다. 문자 "A"는 문자입니다. "%"도 문자입니다. 고유한 숫자 값이 없으며 ASC II, 유니코드 또는 컴퓨터에도 직접 연결되지 않습니다. 기호는 컴퓨터 이전에도 존재했습니다.
코드화된 문자 세트
숫자 값은 문자 모음에 할당됩니다. 특정 문자 인코딩 세트를 사용하여 숫자 결과를 표현할 수 있도록 문자에 코드를 할당합니다. 다른 코딩된 문자 세트는 동일한 문자에 다른 값을 할당할 수 있습니다. 문자 세트 매핑은 일반적으로 USASCII, ISO 8859-1, 유니코드(ISO 10646-1) 및 JIS X0201과 같은 표준 조직에서 결정됩니다.
문자 인코딩 방식
인코딩된 문자 집합 멤버를 옥텟(8비트 바이트)으로 매핑합니다. 인코딩 체계는 문자 인코딩 시퀀스가 바이트 시퀀스로 표현되는 방식을 정의합니다. 문자 인코딩 값은 인코딩 바이트와 동일할 필요가 없으며 일대일 또는 일대다 관계일 필요도 없습니다. 원칙적으로 문자 세트 인코딩 및 디코딩은 객체 직렬화 및 역직렬화로 근사화될 수 있습니다.
일반적으로 문자 데이터 인코딩은 네트워크 전송이나 파일 저장에 사용됩니다. 인코딩 체계는 문자 집합이 아니라 매핑이지만 밀접한 관계로 인해 대부분의 인코딩은 별도의 문자 집합과 연결됩니다. 예를 들어 UTF-8,
유니코드 문자 집합을 인코딩하는 데에만 사용됩니다. 그럼에도 불구하고 하나의 인코딩 체계를 사용하여 여러 문자 집합을 처리하는 것이 가능합니다. 예를 들어 EUC는 여러 아시아 언어의 문자를 인코딩할 수 있습니다.
그림 6-1은 유니코드 문자 시퀀스를 바이트 시퀀스로 인코딩하기 위해 UTF-8 인코딩 체계를 사용하는 그래픽 표현입니다. UTF-8은 0x80보다 작은 문자 코드 값을 단일 바이트 값으로 인코딩합니다(표준 ASC II). 다른 모든 유니코드 문자는 2~6바이트의 멀티바이트 시퀀스로 인코딩됩니다(http://www.ietf.org/rfc/rfc2279.txt).
문자셋
문자 세트라는 용어는 RFC2278(http://ietf.org/rfc/rfc2278.txt)에 정의되어 있습니다. 이는 인코딩된 문자 세트 및 문자 인코딩 체계의 모음입니다. java.nio.charset 패키지의 클래스는 문자 집합 추출을 캡슐화하는 Charset입니다.
1111111111111111
유니코드는 16비트 문자 인코딩입니다. 전 세계 언어의 문자 집합을 하나의 포괄적인 매핑으로 통합하려고 시도합니다. 그 자리를 차지했지만 오늘날 널리 사용되는 다른 문자 인코딩도 많이 있습니다.
대부분의 운영 체제는 I/O 및 파일 저장 측면에서 여전히 바이트 중심이므로 유니코드 또는 기타 인코딩 중 어떤 인코딩을 사용하더라도 여전히 바이트 시퀀스와 문자 집합 인코딩 간에 변환해야 합니다.
java.nio.charset 패키지로 구성된 클래스는 이러한 요구를 충족합니다. Java 플랫폼이 문자 집합 인코딩을 다룬 것은 이번이 처음은 아니지만 가장 체계적이고 포괄적이며 유연한 솔루션입니다. java.nio.charset.spi 패키지는 필요에 따라 인코더와 디코더를 연결할 수 있도록 SPI(서버 프로비저닝 인터페이스)를 제공합니다.
문자 세트: 기본값은 JVM 시작 시 결정되며 기본 운영 체제 환경, 로케일 및/또는 JVM 구성에 따라 다릅니다. 특정 문자 집합이 필요한 경우 가장 안전한 방법은 명시적으로 이름을 지정하는 것입니다. 기본 배포가 개발 환경과 동일하다고 가정하지 마십시오. 문자 세트 이름은 대소문자를 구분하지 않습니다. 즉, 문자 세트 이름을 비교할 때 대문자와 소문자는 동일한 것으로 간주됩니다. IANA(Internet Assigned Names Authority)는 공식적으로 등록된 모든 문자 집합 이름을 유지 관리합니다.
예제 6-1에서는 다양한 Charset 구현을 사용하여 문자를 바이트 시퀀스로 변환하는 방법을 보여줍니다.
예제 6-1. 표준 문자 집합 인코딩 사용
패키지 com.ronsoft.books.nio.charset;
import java.nio.charset.Charset;
import java.nio.ByteBuffer;
/**
* 문자셋 인코딩 테스트. 일부를 포함하는 동일한 입력 문자열을 실행합니다.
* 비ASCII 문자, 여러 Charset 인코더를 통해 16진수 덤프
* 결과 바이트 시퀀스의 값.
*
* @저자 Ron Hitchens ([email protected])
*/
공개 클래스 EncodeTest {
public static void main(String[] argv)에서 예외가 발생합니다.
// 인코딩할 문자 시퀀스입니다.
문자열 입력 = "/u00bfMa/u00f1ana?";
// 인코딩할 문자셋 목록
String[] charsetNames = { "US-ASCII", "ISO-8859-1", "UTF-8",
"UTF-16BE", "UTF-16LE", "UTF-16" // , "X-ROT13"
};
for (int i = 0; i < charsetNames.length; i++) {
doEncode(Charset.forName(charsetNames[i]), 입력);
}
}
/**
* 주어진 Charset 및 입력 문자열에 대해 문자를 인코딩하고
* 결과 바이트 인코딩은 읽을 수 있는 형식입니다.
*/
개인 정적 무효 doEncode(Charset cs, 문자열 입력) {
ByteBuffer bb = cs.encode(입력);
System.out.println("문자 집합: " + cs.name());
System.out.println(" 입력: " + 입력);
System.out.println("인코딩됨: ");
for (int i = 0; bb.hasRemaining(); i++) {
int b = bb.get();
int ival = ((int) b) & 0xff;
char c = (문자) ival;
// 테이블 형식 정렬을 깔끔하게 유지합니다.
만약 (i < 10)
System.out.print(" ");
//인덱스 번호 출력
System.out.print(" " + i + ": ");
// 더 나은 형식의 출력이 언젠가 나올 예정입니다...
if (ival < 16)
System.out.print("0");
// 바이트의 16진수 값을 인쇄합니다.
System.out.print(Integer.toHexString(ival));
// 바이트가 a의 값인 것처럼 보이는 경우
// 인쇄 가능한 문자, 인쇄한다는 보장은 없습니다.
// 그럴 겁니다.
if (Character.isWhitespace(c) || Character.isISOControl(c)) {
System.out.println("");
} 또 다른 {
System.out.println(" (" + c + ")");
}
}
System.out.println("");
}
}
문자 집합: ISO-8859-1
입력: ?Ma?ana?
인코딩됨:
0:20
1: 친구(?)
2:4일(엠)
3:61(a)
4: f1(?)
5:61(a)
6:6e(n)
7:61(a)
8시 3분(?)
문자 집합: UTF-8
입력: ?Ma?ana?
인코딩됨:
0:20
1:c2(?)
2: 친구(?)
3:4일(엠)
4:61(a)
5:c3(?)
6: b1(±)
7:61(a)
8:6e(n)
9:61(a)
10시 3분(?)
문자 집합: UTF-16BE
입력: ?Ma?ana?
인코딩됨:
0:00
1시 20분
2시
3: 친구(?)
4시
5:4일(엠)
6시
7:61(a)
8시
9: f1(?)
10:00
11:61(a)
12:00
13:6e(n)
14:00
15:61(가)
16:00
17:3f(?)
문자 집합: UTF-16LE
입력: ?Ma?ana?
인코딩됨:
0:20
1:00
2: 친구(?)
3시
4:4일(엠)
5시
6:61(a)
7시
8: f1(?)
9:00
10:61(a)
11:00
12:6e(n)
13:00
14:61(가)
15:00
16:3f(?)
17:00
문자 집합: UTF-16
입력: ?Ma?ana?
인코딩됨:
0:페(?)
1: ff(?)
2시
3:20
4시
5: 친구(?)
6시
7:4일(엠)
8시
9:61(a)
10:00
11: f1(?)
12:00
13:61(가)
14:00
15:6e(n)
16:00
17:61(a)
18:00
19:3f(?)
패키지 java.nio.charset;
공개 추상 클래스 Charset은 Comparable을 구현합니다.
{
공개 정적 부울 isSupported(문자열 charsetName)
공개 정적 Charset forName(문자열 charsetName)
공개 정적 SortedMap availableCharsets()
공개 최종 문자열 이름()
공개 최종 별칭 설정()
공개 문자열 표시 이름()
공개 문자열 표시 이름(로케일 로케일)
공개 최종 부울 isRegistered()
공개 부울 canEncode()
공개 추상 CharsetEncoder newEncoder();
공개 최종 ByteBuffer 인코딩(CharBuffer cb)
공개 최종 ByteBuffer 인코딩(문자열 str)
공개 추상 CharsetDecoder newDecoder();
공개 최종 CharBuffer 디코드(ByteBuffer bb)
공개 추상 부울에는 (Charset cs)가 포함되어 있습니다.
공개 최종 부울은 (객체 ob)와 같습니다.
공개 최종 int CompareTo(객체 ob)
공개 최종 int hashCode()
공개 최종 문자열 toString()
}
대부분의 경우 JVM 판매자만이 이러한 규칙에 주의를 기울입니다. 그러나 자신의 문자 집합을 응용 프로그램의 일부로 사용하려는 경우 하지 말아야 할 사항을 아는 것이 도움이 될 것입니다. isRegistered()에 대해 false를 반환하고 "X -"로 시작하는 문자 집합의 이름을 지정해야 합니다.
문자 세트 비교:
공개 추상 클래스 Charset은 Comparable을 구현합니다.
{
// 부분 API 목록입니다.
공개 추상 부울에는 (Charset cs)가 포함되어 있습니다.
공개 최종 부울은 (객체 ob)와 같습니다.
공개 최종 int CompareTo(객체 ob)
공개 최종 int hashCode()
공개 최종 문자열 toString()
}
문자 세트 인코더: 문자 세트는 인코딩된 문자 세트와 관련 인코딩 체계로 구성됩니다. CharsetEncoder 및 CharsetDecoder 클래스는 변환 체계를 구현합니다.
CharsetEncoder API에 대한 참고 사항: 첫째, encode() 형식이 단순할수록 재할당된 ByteBuffer에 제공하는 CharBuffer의 인코딩은 모든 인코딩을 결합합니다. 이는 Charset 클래스에서 encode()를 직접 호출할 때 호출되는 마지막 메서드입니다.
언더플로우
과다
잘못된 입력
매핑할 수 없는 문자
인코딩하는 동안 인코더에 결함이 있거나 매핑할 수 없는 입력이 있으면 결과 개체가 반환됩니다. 또한 개별 문자 또는 문자 시퀀스를 테스트하여 인코딩 가능 여부를 확인할 수도 있습니다. 인코딩이 가능한지 확인하는 방법은 다음과 같습니다.
패키지 java.nio.charset;
공개 추상 클래스 CharsetEncoder
{
// 부분 API 목록입니다.
공개 부울 canEncode(문자 c)
공개 부울 canEncode(CharSequence cs)
}
보고서
CharsetEncoder를 생성할 때의 기본 동작입니다. 이 동작은 앞에서 언급한 CoderResult 개체를 반환하여 코딩 오류를 보고해야 함을 나타냅니다.
무시하다(무시하다)
인코딩 오류를 무시해야 하며 위치가 벗어나면 잘못된 입력이 중단되어야 함을 나타냅니다.
바꾸다
인코딩 오류는 오류 입력을 중단하고 이 CharsetEncoder에 대해 정의된 현재 대체 바이트 시퀀스를 출력하여 처리됩니다.
문자 세트 인코딩은 나중에 디코딩할 준비를 하기 위해 문자를 바이트 시퀀스로 변환한다는 점을 기억하십시오. 대체 시퀀스를 유효한 문자 시퀀스로 디코딩할 수 없는 경우 인코딩된 바이트 시퀀스는 유효하지 않게 됩니다.
CoderResult 클래스: CoderResult 객체는 CharsetEncoder 및 CharsetDecoder 객체에 의해 반환됩니다.
패키지 java.nio.charset;
공개 클래스 CoderResult {
공개 정적 최종 CoderResult 오버플로
공개 정적 최종 CoderResult UNDERFLOW
공개 부울 isUnderflow()
공개 부울 isOverflow()
<span style="white-space:pre"> </span>공개 부울 isError()
공개 부울 isMalformed()
공개 부울 isUnmappable()
공개 정수 길이()
공개 정적 CoderResult MalformedForLength(정수 길이)
공개 정적 CoderResult unmappableForLength(정수 길이)
<span style="white-space:pre"> </span>public void throwException()이 CharacterCodingException을 발생시킵니다.
}
패키지 java.nio.charset;
공개 추상 클래스 CharsetDecoder
{
// 부분 API 목록입니다.
공개 최종 CharsetDecoder 재설정()
공개 최종 CharBuffer 디코드(ByteBuffer in)
CharacterCodingException이 발생합니다.
공개 최종 CoderResult 디코드(ByteBuffer 입력, CharBuffer 출력,
부울 endOfInput)
공개 최종 CoderResult 플러시(CharBuffer 출력)
}
1. 디코더를 입력을 받을 준비가 된 알려진 상태로 만들기 위해 Reset()을 호출하여 디코더를 재설정합니다.
2. endOfInput을 false로 설정하고 디코딩 엔진에 바이트를 제공하기 위해 decode()를 여러 번 호출하거나 호출하지 마십시오. 디코딩이 진행됨에 따라 지정된 CharBuffer에 문자가 추가됩니다.
3. endOfInput을 true로 설정하고 decode()를 한 번 호출하여 모든 입력이 제공되었음을 디코더에 알립니다.
4. 플러시()를 호출하여 디코딩된 모든 문자가 출력으로 전송되었는지 확인합니다.
예제 6-2에서는 문자 세트 인코딩을 나타내는 바이트 스트림을 인코딩하는 방법을 보여줍니다.
예제 6-2. 문자 집합 디코딩
패키지 com.ronsoft.books.nio.charset;
import java.nio.*;
import java.nio.charset.*;
import java.nio.channels.*;
import java.io.*;
/**
* 테스트 문자셋 디코딩.
*
* @저자 Ron Hitchens ([email protected])
*/
공개 클래스 CharsetDecode {
/**
* 일반적인 경우에 문자셋 디코딩을 테스트하여 버퍼 감지 및 처리
* 입력 끝에서 디코더 상태를 언더/오버플로하고 플러시합니다.
* stdin에서 읽고 ASCII로 인코딩된 바이트 스트림을 문자로 디코딩합니다.
* 디코딩된 문자는 stdout에 기록됩니다. 이는 사실상 '고양이'입니다.
* ASCII 파일을 입력하지만 간단히 다른 문자 세트 인코딩을 사용할 수 있습니다.
* 명령줄에서 지정합니다.
*/
public static void main(String[] argv)에서 IOException이 발생합니다.
// 기본 문자 세트는 표준 ASCII입니다.
문자열 charsetName = "ISO-8859-1";
// 문자셋 이름은 명령줄에서 지정할 수 있습니다.
if (인수 길이 > 0) {
charsetName = argv[0];
}
// stdin 주위에 채널을 감싸고, stdout 주위에 채널을 감싸고,
// 명명된 Charset을 찾아 deco de 메소드에 전달합니다.
// 명명된 문자 세트가 유효하지 않으면 유형의 예외가 발생합니다.
// UnsupportedCharsetException이 발생합니다.
decodeChannel(Channels.newChannel(System.in), 새로운 OutputStreamWriter(
System.out), Charset.forName(charsetName));
}
/**
* 채널에서 바이트를 읽고 디코딩하는 범용 정적 메서드
* 그에 따라
*
* @param 소스
* EOF로 읽어올 ReadableByteChannel 객체
* 인코딩된 바이트의 소스.
* @param 작가
* 디코딩된 문자를 쓸 Writer 개체입니다.
* @param 문자셋
* CharsetDecoder가 다음 작업을 수행하는 데 사용되는 Charset 객체
* 문자 세트 디코딩. Java NIO 206
*/
공개 정적 무효 decodeChannel(ReadableByteChannel 소스, 작가 작가,
Charset charset)은 UnsupportedCharsetException, IOException을 발생시킵니다.
// Charset에서 디코더 인스턴스를 가져옵니다.
CharsetDecoder 디코더 = charset.newDecoder();
// 잘못된 문자를 기본 표시로 바꾸도록 디코더에 지시합니다.
decoder.onMalformedInput(CodingErrorAction.REPLACE);
decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
// 근본적으로 다른 입력 및 출력 버퍼 크기를 할당합니다.
// 테스트 목적으로
ByteBuffer bb = ByteBuffer.allocateDirect(16 * 1024);
CharBuffer cb = CharBuffer.allocate(57);
// 버퍼가 비어 있으면 입력이 필요함을 나타냅니다.
CoderResult 결과 = CoderResult.UNDERFLOW;
부울 eof = false;
동안 (!eof) {
// 입력 버퍼 언더플로우가 더 많은 입력을 원합니다.
if (결과 == CoderResult.UNDERFLOW) {
// 디코더는 모든 입력을 소비하고 리필을 준비합니다.
bb.clear();
// 입력 버퍼를 채우고 EOF를 확인합니다.
eof = (source.read(bb) == -1);
// 디코더가 읽을 버퍼를 준비합니다.
bb.플립();
}
// 입력 바이트를 디코딩하여 EOF 플래그를 전달합니다.
결과 = decoder.decode(bb, cb, eof);
// 출력 버퍼가 가득 차면 출력을 비웁니다.
if (결과 == CoderResult.OVERFLOW) {
rainCharBuf(cb,작성기);
}
}
// 디코더에서 남은 상태를 모두 플러시합니다. 주의하세요.
// 출력 버퍼 오버플로를 감지합니다.
while (decoder.flush(cb) == CoderResult.OVERFLOW) {
rainCharBuf(cb,작성기);
}
// 출력 버퍼에 남아 있는 모든 문자를 비웁니다.
rainCharBuf(cb,작성기);
// 채널을 닫습니다. 버퍼링된 데이터를 표준 출력으로 푸시합니다.
소스.닫기();
작가.플러시();
}
/**
* char 버퍼를 비우고 그 내용을 주어진 버퍼에 쓰는 도우미 메서드
* Writer 객체 반환 시 버퍼는 비어 있고 다시 채울 준비가 되어 있습니다.
*
* @param cb
* 쓸 문자가 포함된 CharBuffer입니다.
* @param 작가
* cb의 문자를 소비하는 Writer 개체입니다.
*/
static voidrainCharBuf(CharBuffer cb, Writerwriter)가 IOException을 발생시킵니다.
cb.flip(); // 배수를 위한 버퍼 준비
// CharBuffer에 포함된 문자를 쓰지만
// 실제로 버퍼의 상태를 수정하지는 않습니다.
// get() 호출로 인해 char 버퍼가 소모된 경우,
// 여기에 루프가 필요할 수 있습니다.
if (cb.hasRemaining()) {
writer.write(cb.toString());
}
cb.clear(); // 다시 채워질 버퍼를 준비합니다.
}
}
API를 탐색하기 전에 Charset SPI의 작동 방식을 설명하는 것이 중요합니다. java.nio.charset.spi 패키지에는 CharsetProvider라는 하나의 추출 클래스만 포함되어 있습니다. 이 클래스의 구체적인 구현은 제공하는 Charset 객체와 관련된 정보를 제공합니다. 사용자 정의 문자 세트를 정의하려면 먼저 java.nio.charset 패키지에서 Charset, CharsetEncoder 및 CharsetDecoder의 특정 구현을 생성해야 합니다. 그런 다음 해당 클래스를 JVM에 제공할 CharsetProvider의 사용자 정의 하위 클래스를 만듭니다.
사용자 정의 문자 세트를 생성합니다:
최소한 해야 할 일은 java.nio.charset.Charset의 하위 클래스를 만들고 세 가지 추출 메서드와 생성자의 구체적인 구현을 제공하는 것입니다. Charset 클래스에는 매개 변수가 없는 기본 생성자가 없습니다. 이는 매개변수를 허용하지 않더라도 사용자 정의 문자 세트 클래스에 생성자가 있어야 함을 의미합니다. 이는 인스턴스화 시(생성자 시작 부분에서 super()를 호출하여) Charset의 생성자를 호출하여 charset 사양 이름과 별칭을 제공해야 하기 때문입니다. 이렇게 하면 Charset 클래스의 메서드가 이름 관련 작업을 처리할 수 있으므로 좋은 것입니다.
마찬가지로 CharsetEncoder 및 CharsetDecoder의 구체적인 구현을 제공해야 합니다. 문자 집합은 인코딩된 문자와 인코딩/디코딩 체계의 모음이라는 점을 기억하세요. 이전에 살펴본 것처럼 인코딩과 디코딩은 API 수준에서 거의 대칭입니다. 인코더를 구현하는 데 필요한 사항에 대한 간략한 설명은 다음과 같습니다. 디코더 구축에도 동일하게 적용됩니다.
Charset과 마찬가지로 CharsetEncoder에는 기본 생성자가 없으므로 구체적인 클래스 생성자에서 super()를 호출하여 필수 매개변수를 제공해야 합니다.
자체 CharsetEncoder 구현을 제공하려면 최소한 구체적인 encodeLoop() 메소드를 제공해야 합니다. 간단한 인코딩 알고리즘의 경우 다른 메서드의 기본 구현이 제대로 작동합니다. encodeLoop()는 Boolean 플래그를 제외하고 encode()와 유사한 매개변수를 취합니다. encode() 메소드는 encodeLoop()에 대한 실제 인코딩을 나타냅니다. 이는 CharBuffer 매개변수에서 사용된 문자에만 주의를 기울이고 인코딩된 바이트를 제공된 ByteBuffer에 출력하면 됩니다.
이제 관련 인코더 및 디코더를 포함하여 사용자 정의 문자 세트를 구현하는 방법을 살펴보았으므로 이를 사용하여 코드를 실행할 수 있도록 이를 JVM에 연결하는 방법을 살펴보겠습니다.
사용자 정의 문자 세트를 제공하십시오.
JVM 런타임 환경에 고유한 Charset 구현을 제공하려면 java.nio.charsets.-spi에서 CharsetProvider 클래스의 구체적인 하위 클래스를 각각 매개변수 없는 생성자로 생성해야 합니다. 구성 파일의 정규화된 이름을 읽어 CharsetProvider 클래스를 찾을 수 있으므로 매개 변수가 없는 생성자가 중요합니다. 그런 다음 이 클래스 이름 문자열을 Class.newInstance()로 가져와 공급자를 인스턴스화합니다. 이는 매개 변수가 없는 생성자를 통해서만 작동합니다.
JVM이 읽은 구성 파일은 java.nio.charset.spi.CharsetProvider라는 문자 집합 공급자를 찾습니다. JVM 클래스 경로의 소스 디렉터리(META-INF/services)에 있습니다. 각 JAR(JavaArchive)에는 해당 JAR의 클래스 및 리소스에 대한 정보가 포함된 META-INF 디렉터리가 있습니다. META-INF라는 디렉토리는 JVM 클래스 경로의 일반 디렉토리 상단에 배치될 수도 있습니다.
CharsetProvider API는 거의 쓸모가 없습니다. 사용자 정의 문자 집합을 제공하는 실제 작업은 사용자 정의 Charset, CharsetEncoder 및 CharsetDecoder 클래스를 생성할 때 발생합니다. CharsetProvider는 단순히 문자 집합과 런타임 환경 사이를 촉진하는 역할을 합니다.
예제 6-3에서는 문자 집합 사용법, 인코딩 및 디코딩, Charset SPI를 보여주는 샘플링 코드를 포함하여 사용자 정의 Charset 및 CharsetProvider의 구현을 보여줍니다. 예제 6-3에서는 사용자 정의 Charset을 구현합니다.
예제 6-3. 사용자 정의된 Rot13 문자 집합
패키지 com.ronsoft.books.nio.charset;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
java.util.Map 가져오기;
import java.util.Iterator;
import java.io.Writer;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileReader;
/**
* Rot13 인코딩을 수행하는 Charset 구현은
* 알파벳 문자를 13만큼 이동하는 간단한 텍스트 난독화 알고리즘
* 'a'가 'n'이 되고, 'o'가 'b'가 되는 식으로 이 알고리즘이 대중화되었습니다.
* 수년 전 유즈넷 토론 포럼에서 음란한 단어를 가리고 숨기기 위해
* 질문에 대한 답변 등 Rot13 알고리즘은 대칭적입니다.
* Rot13에 의해 스크램블된 텍스트로 원본을 제공합니다.
* 스크램블되지 않은 텍스트.
*
* 이 Charset 인코딩을 출력 스트림에 적용하면 모든 결과가 발생합니다.
* 해당 스트림에 기록된 대로 Rot13이 스크램블되도록 기록하고 적용합니다.
* 입력 스트림에 대한 데이터 읽기는 읽힐 때 Rot13 디스크램블됩니다.
*
* @저자 Ron Hitchens ([email protected])
*/
공개 클래스 Rot13Charset은 Charset을 확장합니다.
// 우리가 위임한 기본 문자셋 인코딩의 이름
개인 정적 최종 문자열 BASE_CHARSET_NAME = "UTF-8";
// 트랜스코딩에 사용할 실제 문자 세트를 처리합니다.
// 문자 및 바이트를 사용하면 Rot13을 적용할 수 있습니다.
// 멀티바이트 문자 집합 인코딩 알고리즘입니다.
// 기본 인코딩에 관계없이 ASCII 알파 문자가 회전됩니다.
Charset baseCharset;
/**
* Rot13 charset의 생성자 슈퍼클래스 생성자를 호출합니다.
* 우리가 알 수 있는 이름을 전달한 다음 해당 항목에 대한 참조를 저장하십시오.
* 문자셋을 위임합니다.
*/
protected Rot13Charset(String canonical, String[] 별칭) {
super(표준, 별칭);
// 위임할 기본 문자 세트를 저장합니다.
baseCharset = Charset.forName(BASE_CHARSET_NAME);
}
//------------------------------------------------ ----------
/**
* 이 구현을 얻기 위해 이 Charset의 사용자가 호출합니다.
* 비공개 클래스(아래 정의)의 인스턴스를 인스턴스화하고 전달합니다.
* 기본 Charset의 인코더.
*/
공개 CharsetEncoder newEncoder() {
새로운 Rot13Encoder(this, baseCharset.newEncoder())를 반환합니다.
}
/**
* 이 구현은 디코더를 얻기 위해 이 Charset의 사용자에 의해 호출됩니다.
* 비공개 클래스(아래 정의)의 인스턴스를 인스턴스화하고 전달합니다.
* 기본 Charset의 디코더.
*/
공개 CharsetDecoder newDecoder() {
return new Rot13Decoder(this, baseCharset.newDecoder());
}
/**
* 이 메소드는 구체적인 Charset으로 구현되어야 합니다.
* 안전합니다.
*/
공개 부울 포함(Charset cs) {
반환(거짓);
}
/**
* 주어진 문자열에서 모든 ASCII 알파 문자를 회전하는 일반적인 루틴
* CharBuffer by 13. 이 코드는 상위 및 상위를 명시적으로 비교합니다.
* 메소드를 사용하는 대신 소문자 ASCII 문자를 사용합니다.
* Character.isLowerCase 및 Character.isUpperCase가 있기 때문입니다.
* 13으로 회전 구성표는 다음의 알파벳 문자에 대해서만 제대로 작동합니다.
* ASCII 문자 집합과 해당 메서드는 비ASCII 유니코드에 대해 true를 반환할 수 있습니다.
* 문자.
*/
개인 무효 rot13(CharBuffer cb) {
for (int pos = cb.position(); pos < cb.limit(); pos++) {
char c = cb.get(pos);
char a = '/u0000';
// 소문자 알파인가요?
if ((c >= 'a') && (c <= 'z')) {
a = 'a';
}
// 대문자 알파인가요?
if ((c >= 'A') && (c <= 'Z')) {
a = 'A';
}
// 둘 중 하나라면 13만큼 굴립니다.
if (a != '/u0000') {
c = (문자) ((((c - a) + 13) % 26) + a);
cb.put(pos, c);
}
}
}
//------------------------------------------------ --------
/**
* Rot13 Chars et.에 대한 인코더 구현입니다.
* 아래 일치하는 디코더 클래스는 "impl" 메소드도 재정의해야 합니다.
* implOnMalformedInput()과 같은 패스스루 호출을 수행합니다.
* baseEncoder 객체. 이는 해커의 연습 과제로 남겨집니다.
*/
개인 클래스 Rot13Encoder는 CharsetEncoder를 확장합니다.
개인 CharsetEncoder baseEncoder;
/**
* 생성자, Charset 객체를 사용하여 슈퍼클래스 생성자를 호출합니다.
* 및 대리자 인코더의 인코딩 크기입니다.
*/
Rot13Encoder(Charset cs, CharsetEncoder baseEncoder) {
super(cs, baseEncoder.averageBytesPerChar(), baseEncoder
.maxBytesPerChar());
this.baseEncoder = baseEncoder;
}
/**
* 인코딩 루프 구현 먼저 Rot13을 적용합니다.
* 알고리즘을 CharBuffer로 스크램블링한 다음 인코더를 재설정합니다.
* 기본 Charset을 만들고 encode() 메소드를 호출하여 실제 작업을 수행합니다.
* 인코딩. 라틴어가 아닌 문자 집합에서는 제대로 작동하지 않을 수 있습니다.
* 전달된 CharBuffer는 읽기 전용이거나 호출자가 다음에 대해 재사용할 수 있습니다.
* 다른 목적으로 복제하고 Rot13 인코딩을 적용합니다.
* 복사합니다. 우리는 입력 버퍼의 위치를 다음으로 전진시키고 싶습니다.
* 소비된 문자를 반영합니다.
*/
protected CoderResult encodeLoop(CharBuffer cb, ByteBuffer bb) {
CharBuffer tmpcb = CharBuffer.allocate(cb.remaining());
동안(cb.hasRemaining()) {
tmpcb.put(cb.get());
}
tmpcb.rewind();
rot13(tmpcb);
baseEncoder.reset();
CoderResult cr = baseEncoder.encode(tmpcb, bb, true);
// 오류나 출력 오버플로가 발생하면 조정해야 합니다.
// 일치하는 입력 버퍼의 위치
// 실제로 임시 버퍼에서 소비되었습니다.
// 언더플로(모든 입력이 소비됨), 이는 작동하지 않습니다.
cb.position(cb.position() - tmpcb.remaining());
반환(cr);
}
}
//------------------------------------------------ --------
/**
* Rot13 Charset에 대한 디코더 구현입니다.
*/
개인 클래스 Rot13Decoder는 CharsetDecoder를 확장합니다.
개인 CharsetDecoder baseDecoder;
/**
* 생성자, Charset 객체를 사용하여 슈퍼클래스 생성자를 호출합니다.
* 대리자 디코더의 문자/바이트 값을 전달합니다.
*/
Rot13Decoder(Charset cs, CharsetDecoder baseDecoder) {
super(cs, baseDecoder.averageCharsPerByte(), baseDecoder
.maxCharsPerByte());
this.baseDecoder = 베이스디코더;
}
/**
* 디코딩 루프 구현 먼저 디코더를 재설정합니다.
* 기본 문자 집합을 호출하여 바이트를 문자로 디코딩합니다.
* 결과 코드를 저장하면 CharBuffer가 해독됩니다.
* Rot13 알고리즘과 결과 코드가 반환되지 않을 수 있습니다.
* 비라틴어 문자 집합에 적합합니다.
*/
protected CoderResult decodeLoop(ByteBuffer bb, CharBuffer cb) {
baseDecoder.reset();
CoderResult result = basedecoder.decode (bb, cb, true);
ROT13 (CB);
반환 (결과);
}
}
// ---------------------------------------------------- --------
/**
* ROT13 Charset에 대한 단위 테스트는 입력을 열고 읽습니다
* 명령 줄에 명명 된 경우 파일 또는 args가 제공되지 않은 경우 stdin 및
* x -rot13 charset 인코딩을 통해 내용을 stdout에 쓰십시오
* ROT13 알고리즘에 의해 구현 된 "암호화"는 대칭입니다
* 예를 들어 Java 소스 코드와 같은 일반 텍스트 파일에서
* 스크램블 버전. 스크램블 버전을 다시 먹이면
* 원본 일반 텍스트 문서.
*/
public static void main (string [] argv)은 예외 {
버퍼링 리더;
if (argv. 길이> 0) {
// 이름이 지정된 파일을 엽니 다
in = new BufferedReader (New Filereader (Argv [0]);
} 또 다른 {
// stdin 주위에 버퍼드 리더를 감싸십시오
in = new bufferedReader (new inputStreamReader (System.In));
}
// ROT13 인코딩을 사용하는 PrintStream을 만듭니다
PrintStream out = New PrintStream (System.out, False, "x -rot13");
문자열 s = null;
// 모든 입력을 읽고 출력에 씁니다.
// 데이터가 PrintStream을 통과함에 따라
// ROT13으로 인코딩됩니다.
while ((s = in.readline ())! = null) {
out.println (s);
}
out.flush();
}
}
예 6-4. 사용자 정의 문자 세트 제공자
패키지 com.ronsoft.books.nio.charset;
import java.nio.charset.charset;
import java.nio.charset.spi.charsetprovider;
import java.util.HashSet;
import java.util.iterator;
/**
* charsets를 제공하는 charsetprovider 클래스
* Ronsoft. 현재 x -rot13 charset 만 있습니다
* 등록 된 IANA 숯이 아니므로 이름이 "X-"로 시작됩니다.
* 외부 숯불로 충돌합니다.
*
*이 charsetprovider를 활성화하려면 파일을 추가해야합니다.
* 다음 위치에서 JVM 런타임의 클래스 경로 :
* Meta-Inf/Services/java.nio.charsets.spi.charsetprovider
*
* 해당 파일은이 클래스의 자격을 갖춘 이름을 가진 줄을 포함해야합니다.
* 자체 라인 자체 : com.ronsoft.books.nio.charset.ronsoftcharsetprovider java
* NIO 216
*
* java.nio.charsets.spi.charsetprovider의 javadoc 페이지를 참조하십시오
* 세부 사항.
*
* @Author Ron Hitchens ([email protected])
*/
공개 클래스 RonsoftCharsetProvider 확장 charsetprovider {
// 우리가 제공하는 숯불 이름
비공개 정적 최종 문자열 charset_name = "xot13";
// charset 객체에 대한 핸들
개인 숯으로 ROT13 = NULL;
/**
* 생성자, charset 객체를 인스턴스화하고 참조를 저장하십시오.
*/
Public RonsoftCharsetProvider () {
this.rot13 = new Rot13charset (charset_name, new String [0]);
}
/**
* charset 정적 메소드가 호출하여 이름이 지정된 charset을 찾습니다
*이 숯불의 이름입니다 (우리는 별명이 없습니다)를 반환합니다.
* rot13 charset, 그렇지 않으면 null을 반환합니다.
*/
public charset charsetforname (string charsetname) {
if (charsetname.equalsignorecase (charset_name)) {
반환 (ROT13);
}
반환 (null);
}
/**
* 우리가 제공하는 숯불 객체 세트에 반복기를 반환하십시오.
*
* @모든 숯에 대한 참조가 포함 된 반복자 개체
*이 클래스에서 제공 한 객체.
*/
public iterator <charset> charsets () {
해시 세트 <charset> set = new Hashset <CharSet> (1);
set.add (rot13);
return (set.iterator ());
}
}
예 6-1의 문자 세트 목록에 x -rot13을 추가하면이 추가 출력이 생성됩니다.
숯불 : XOT13
입력 : 자마나?
인코딩 :
0 : C2 (‰)
1 : BF (‰)
2 : 5A (Z)
3 : 6E (N)
4 : C3 (Ă)
5 : B1 (±)
6 : 6E (N)
7:61 (a)
8 : 6E (N)
9 : 3F (?)
숯불 (캐릭터 세트 클래스)
바이트 시퀀스로서 세트와 다른 문자 시퀀스를 나타내는 데 사용되는 인코딩을 캡슐화하는 문자 세트 인코딩 방식.
CharsetEncoder (캐릭터 세트 인코딩 클래스)
엔진 인코딩 엔진은 문자 시퀀스를 바이트 시퀀스로 변환합니다. 그런 다음 바이트 시퀀스를 디코딩하여 소스 문자 시퀀스를 재구성 할 수 있습니다.
CharsetDecoder (charset decoder class)
디코딩 엔진은 인코딩 된 바이트 시퀀스를 문자 시퀀스로 변환합니다.
CharsetProvider SPI (Charset Provider SPI)
런타임 환경에서 사용하기 위해 서버 공급 업체 메커니즘을 통해 Charset 구현을 찾아서 사용할 수 있도록하십시오.