失樂園

          技術(shù)之路

          BlogJava 聯(lián)系 聚合 管理
            19 Posts :: 44 Stories :: 40 Comments :: 0 Trackbacks
                 大多數(shù)文本編輯器在打開文件時(shí)都能夠自動(dòng)檢測(cè)文件的編碼,那它是怎樣做到的?我雖然沒有實(shí)現(xiàn)過一個(gè)文本編輯器,但是可以猜測(cè)的是,它有一個(gè)默認(rèn)的編碼集合,然后嘗試用每一個(gè)編碼去解碼打開的文件,如果能夠解碼則表示這就是文件的正確編碼。有一些特殊情況,有些編碼在文件開頭有特殊的標(biāo)記字節(jié),因而可以很快檢測(cè),這里不考慮。現(xiàn)在的核心問題就是如何決定一個(gè)編碼是否能夠解碼一個(gè)文件,在Java1.4中可以利用nio中的Charset來解決這個(gè)問題。
          /**
          * 測(cè)試輸入字節(jié)流是否能夠使用指定的字符集解碼。
          */
          public static boolean canDecode(InputStream input, Charset charset) throws IOException {  
              ReadableByteChannel channel = Channels.newChannel(input);  
              CharsetDecoder decoder = charset.newDecoder();  

              ByteBuffer byteBuffer = ByteBuffer.allocate(2048);  
              CharBuffer charBuffer = CharBuffer.allocate(1024);  

              boolean endOfInput = false;  
              while (!endOfInput) {  
                  int n = channel.read(byteBuffer);  
                  byteBuffer.flip(); // flip so it can be drained  
                    
                  endOfInput = (n == -1);  
                  CoderResult coderResult = decoder.decode(byteBuffer, charBuffer, endOfInput);  
                  charBuffer.clear();  
                  if (coderResult == CoderResult.OVERFLOW) {  
                      while (coderResult == CoderResult.OVERFLOW) {  
                          coderResult = decoder.decode(byteBuffer, charBuffer, endOfInput);  
                          charBuffer.clear();  
                      }  
                  }  
                  if (coderResult.isError()) {  
                      return false;  
                  }  
                  byteBuffer.compact(); // compact so it can be refilled  
              }  
              CoderResult coderResult;  
              while ((coderResult = decoder.flush(charBuffer)) == CoderResult.OVERFLOW) {  
                  charBuffer.clear();  
              }  
              if (coderResult.isError()) {  
                  return false;  
              }  
                
              return true;  
          }
          要理解上面的代碼必須熟悉對(duì)Buffer和Channel的操作以及解碼的過程。上面的代碼只是決定能不能解碼,下面代碼能夠解碼出的內(nèi)容寫到字符輸出流中(也就是Writer),它要更復(fù)雜一些。
          Java代碼
          /**
          * 使用指定的字符集解碼字節(jié)輸入流,并將它寫入到字符輸出流中,如果發(fā)生解碼錯(cuò)誤則返回false,否則返回true,
          * 輸入中的無效字節(jié)序列將被忽略。
          */
          public static boolean decode(InputStream input, Writer output, Charset charset) throws IOException {  
              ReadableByteChannel channel = Channels.newChannel(input);  
              CharsetDecoder decoder = charset.newDecoder();  

              ByteBuffer byteBuffer = ByteBuffer.allocate(2048);  
              CharBuffer charBuffer = CharBuffer.allocate(1024);  

              boolean endOfInput = false;  
              boolean error = false;  
              while (!endOfInput) {  
                  int n = channel.read(byteBuffer);  
                  byteBuffer.flip(); // flip so it can be drained  
                    
                  endOfInput = (n == -1);  
                  CoderResult coderResult = decoder.decode(byteBuffer, charBuffer, endOfInput);  
                  error = drainCharBuffer(error, byteBuffer, charBuffer, coderResult, output);  
                  if (coderResult != CoderResult.UNDERFLOW) {  
                      while (coderResult != CoderResult.UNDERFLOW) {  
                          coderResult = decoder.decode(byteBuffer, charBuffer, endOfInput);  
                          error = drainCharBuffer(error, byteBuffer, charBuffer, coderResult, output);  
                      }  
                  }  
                  byteBuffer.compact(); // compact so it can be refilled  
              }  
              CoderResult coderResult;  
              while ((coderResult = decoder.flush(charBuffer)) != CoderResult.UNDERFLOW) {  
                  error = drainCharBuffer(error, byteBuffer, charBuffer, coderResult, output);  
              }  
              error = drainCharBuffer(error, byteBuffer, charBuffer, coderResult, output);  
                
              output.flush();  
              return !error;  
          }  

          private static boolean drainCharBuffer(boolean error, ByteBuffer byteBuffer,   
                  CharBuffer charBuffer, CoderResult coderResult, Writer output) throws IOException {  
              // write charBuffer to output  
              charBuffer.flip();  
              if (charBuffer.hasRemaining())  
                  output.write(charBuffer.toString());  
              charBuffer.clear();  
                
              if (coderResult.isError()) {  
                  error = true;  
                  byteBuffer.position(byteBuffer.position() + coderResult.length()); // ignore invalid byte sequence  
              }  
              return error;  
          }

          要注意byteBuffer的大小不能太小以至于比一個(gè)字符的最大字節(jié)數(shù)還要小,比如說utf-8的每個(gè)字符最多可能占用4個(gè)字節(jié),如果設(shè)置byteBuffer的大小為3,解碼結(jié)果可能總是CoderResult.UNDERFLOW,但是又無法再往byteBuffer填充數(shù)據(jù),因而會(huì)出現(xiàn)死循環(huán)。

          另外要注意的是,程序可能得到錯(cuò)誤的結(jié)果,如:
          String s = "abc中國";  
          byte[] utf8Bytes = s.getBytes(Charset.forName("utf-8"));  
          byte[] gbkBytes = s.getBytes(Charset.forName("gbk"));  
          CharArrayWriter writer = new CharArrayWriter();  
          System.out.println(decode(new ByteArrayInputStream(utf8Bytes), writer, Charset.forName("utf-8")));  
          System.out.println(writer.toString());  
          writer = new CharArrayWriter();  
          System.out.println(decode(new ByteArrayInputStream(utf8Bytes), writer, Charset.forName("gbk")));  
          System.out.println(writer.toString());  

          輸出結(jié)果:
          Java代碼
          true
          abc中國  
          true
          abc涓 浗

          可以看到用utf-8編碼的字節(jié)流仍然可以用gbk進(jìn)行解碼,但是解碼的結(jié)果卻不對(duì)。這是偶然情況,將字符串換成"中國人",則用gbk就不能解碼了。
          posted on 2010-08-17 09:41 狄浩 閱讀(17391) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 漳州市| 博野县| 崇明县| 磐安县| 霍林郭勒市| 新巴尔虎右旗| 桦甸市| 视频| 新沂市| 武隆县| 邯郸县| 麻阳| 开化县| 瓮安县| 民丰县| 临江市| 广德县| 肥城市| 郧西县| 城步| 卓尼县| 柯坪县| 措美县| 达孜县| 青铜峡市| 和林格尔县| 忻州市| 万荣县| 香港| 从江县| 星子县| 赞皇县| 庆阳市| 万山特区| 青州市| 大理市| 育儿| 浦东新区| 兴宁市| 弥勒县| 鄂尔多斯市|