要製作一個 Charset 一定要瞭解 Java 的字串核心,如果之前是寫 C 語言的朋友,首先要認知 char 跟 btye 是不一樣的,在 Java 的 char 是固定 Unicode 編碼,所有輸入都會轉成 Unicode,輸出時在轉成指定編碼,如下圖:

那麼要實做哪些東西呢??
- class Charset
- 定義 Charset 的名稱跟提供 Encoder 跟 Decoder,跟據 IANA 的規範自定的 Charset 名稱必須以
X-
或x-
開頭。 - class CharsetDecoder
- 字符解碼器負責將
byte[]
轉換成char[]
。 - class CharsetEncoder
- 字符編碼器負責將
char[]
轉換成byte[]
。 - class CharsetProvider
- Charset 提供者,以名稱提供 Charset,在
Charset.forName("xxx")
調用時會尋訪所有的 Provider 來取得 Charset。 - META-INF/services/java.nio.charset.spi.CharsetProvider
- 告知 JVM 將
CharsetProvider
註冊到延伸清單中,JVM 在啟動的時候會掃描所有這個路徑的檔案。
最後再將
{Charset}.jar
檔複製到 {jre}/lib/ext
目錄下就部署完畢Big5_Extend
- public class Big5_Extend extends Charset {
- private static final String BASE_CHARSET = "Big5";
- private static final String NAME = "X-Big5-Extend";
- private static final String[] ALIASES = { "X-Big5_Extend" };
- private Charset baseCharset;
- public Big5_Extend() {
- this(NAME, ALIASES);
- }
- public Big5_Extend(String canonical, String[] aliases) {
- super(canonical, aliases);
- baseCharset = Charset.forName(BASE_CHARSET);
- }
- public boolean contains(Charset cs) {
- return this.getClass().isInstance(cs) ||
- baseCharset.getClass().isInstance(cs);
- }
- public CharsetDecoder newDecoder() {
- return new Decoder(this, baseCharset.newDecoder());
- }
- public CharsetEncoder newEncoder() {
- return new Encoder(this, baseCharset.newEncoder());
- }
- // ...
- }
解碼處理
- protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
- base.reset(); /* 重置狀態 */
- /* 先用原生的 Big5 進行解碼 */
- CoderResult result = base.decode(in, out, true);
- if(!result.isUnmappable() || in.remaining() < 2){ return result; }
- /* 無法轉換,進一步使用自訂的解碼 */
- int pos = in.position();
- char big5Char = (char)(in.get(pos) << 8 | in.get(pos + 1));
- char outChar;
- switch (big5Char) {
- case '\uFA40': outChar = '\u5803'; break; /* 堃 */
- case '\uFA41': outChar = '\u83D3'; break; /* 菓 */
- case '\uFA42': outChar = '\u854B'; break; /* 蕋 */
- case '\uFA43': outChar = '\u4F8A'; break; /* 侊 */
- default: return result; /* 不在清單內直接回傳 */
- }
- out.put(outChar);
- in.position(pos + 2);
- return decodeLoop(in, out); /* 遞迴處理*/
- }
編碼處理
- protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
- base.reset(); /* 重置狀態 */
- /* 先用原生的 Big5 進行編碼 */
- CoderResult result = base.encode(in, out, true);
- if(!result.isUnmappable() || out.remaining() < 2){ return result; }
- /* 無法轉換,進一步使用自訂的編碼 */
- int pos = in.position();
- char uniChar = in.get(pos);
- char outChar;
- switch (uniChar) {
- case '\u5803': outChar = '\uFA40'; break; /* 堃 */
- case '\u83D3': outChar = '\uFA41'; break; /* 菓 */
- case '\u854B': outChar = '\uFA42'; break; /* 蕋 */
- case '\u4F8A': outChar ='\uFA43'; break; /* 侊 */
- default: return result; /* 不在清單內直接回傳 */
- }
- out.put((byte)(outChar >> 8));
- out.put((byte)outChar);
- in.position(pos + 1);
- return encodeLoop(in, out); /* 遞迴處理*/
- }
CoderResult 四種狀態
- UNDERFLOW 欠位
- OVERFLOW 溢位
- MALFORMED 有缺陷的輸入
- UNMAPPABLE 無映射字符
完整的 Big5_Extend
- package com.custom.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;
- public class Big5_Extend extends Charset {
- private static final String BASE_CHARSET = "Big5";
- private static final String NAME = "X-Big5-Extend";
- private static final String[] ALIASES = { "X-Big5_Extend" };
- private Charset baseCharset;
- public Big5_Extend() {
- this(NAME, ALIASES);
- }
- public Big5_Extend(String canonical, String[] aliases) {
- super(canonical, aliases);
- baseCharset = Charset.forName(BASE_CHARSET);
- }
- public boolean contains(Charset cs) {
- return this.getClass().isInstance(cs) ||
- baseCharset.getClass().isInstance(cs);
- }
- public CharsetDecoder newDecoder() {
- return new Decoder(this, baseCharset.newDecoder());
- }
- public CharsetEncoder newEncoder() {
- return new Encoder(this, baseCharset.newEncoder());
- }
- private class Decoder extends CharsetDecoder {
- /* Java 原生的 Big5 解碼器 */
- private final CharsetDecoder base;
- Decoder(Charset cs, CharsetDecoder base) {
- super(cs, base.averageCharsPerByte(), base.maxCharsPerByte());
- this.base = base;
- }
- @Override
- protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
- base.reset(); /* 重置狀態 */
- /* 先用原生的 Big5 進行解碼 */
- CoderResult result = base.decode(in, out, true);
- if(!result.isUnmappable() || in.remaining() < 2){ return result; }
- /* 無法轉換,進一步使用自訂的解碼 */
- int pos = in.position();
- char big5Char = (char)(in.get(pos) << 8 | in.get(pos + 1));
- char outChar;
- switch (big5Char) {
- case '\uFA40': outChar = '\u5803'; break; /* 堃 */
- case '\uFA41': outChar = '\u83D3'; break; /* 菓 */
- case '\uFA42': outChar = '\u854B'; break; /* 蕋 */
- case '\uFA43': outChar = '\u4F8A'; break; /* 侊 */
- default: return result; /* 不在清單內直接回傳 */
- }
- out.put(outChar);
- in.position(pos + 2);
- return decodeLoop(in, out);
- }
- }
- private class Encoder extends CharsetEncoder {
- /* Java 原生的 Big5 編碼器 */
- private final CharsetEncoder base;
- Encoder(Charset cs, CharsetEncoder base) {
- super(cs, base.averageBytesPerChar(), base.maxBytesPerChar());
- this.base = base;
- }
- @Override
- protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
- base.reset(); /* 重置狀態 */
- /* 先用原生的 Big5 進行編碼 */
- CoderResult result = base.encode(in, out, true);
- if(!result.isUnmappable() || out.remaining() < 2){ return result; }
- /* 無法轉換,進一步使用自訂的編碼 */
- int pos = in.position();
- char uniChar = in.get(pos);
- char outChar;
- switch (uniChar) {
- case '\u5803': outChar = '\uFA40'; break; /* 堃 */
- case '\u83D3': outChar = '\uFA41'; break; /* 菓 */
- case '\u854B': outChar = '\uFA42'; break; /* 蕋 */
- case '\u4F8A': outChar ='\uFA43'; break; /* 侊 */
- default: return result; /* 不在清單內直接回傳 */
- }
- out.put((byte)(outChar >> 8));
- out.put((byte)outChar);
- in.position(pos + 1);
- return encodeLoop(in, out);
- }
- }
- }
CharsetProvider
- package com.custom.nio.charset;
- import java.nio.charset.Charset;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Map;
- /** 字元編碼器連結器,用來向 JVM 提交自訂的編碼器
- */
- public class CharsetProvider extends java.nio.charset.spi.CharsetProvider {
- static Map<String, Charset> name2charset;
- static Collection<Charset> charsets;
- public Charset charsetForName(String charsetName) {
- if (charsets == null){ init(); }
- return name2charset.get(charsetName.toLowerCase());
- }
- public Iterator<Charset> charsets() {
- if (charsets == null){ init(); }
- return charsets.iterator();
- }
- void init() {
- name2charset = new HashMap<String, Charset>();
- charsets = new HashSet<Charset>();
- charsets.add(new Big5_Extend());
- for (Charset charset : charsets) {
- name2charset.put(charset.name().toLowerCase(), charset);
- for (String aliase: charset.aliases()) {
- name2charset.put(aliase.toLowerCase(), charset);
- }
- }
- }
- }
java.nio.charset.spi.CharsetProvider
- com.custom.nio.charset.CharsetProvider
測試
- public class Test {
- public static void main(String[] args) throws Throwable {
- String charset = "X-Big5-Extend";
- String source = "堃菓蕋侊";
- byte[] bytes = source.getBytes(charset);
- for (byte b : bytes) {
- System.out.printf("%x ", b);
- }
- System.out.println("\n");
- // fa 40 fa 41 fa 42 fa 43
- String result = new String(bytes, charset);
- System.out.println(result);
- // 堃菓蕋侊
- }
- }
參考自:Java字符编码解码的实现详解_java_脚本之家
0 回應:
張貼留言