編碼與解碼

編碼與解碼

  • 碼表: 常見的碼表如下:

    • ASCII : 美國標(biāo)準(zhǔn)信息交換碼。用一個字節(jié)的7位可以表示土砂。
    • ISO8859-1 : 拉丁碼表州既。歐洲碼表,用一個字節(jié)的8位表示萝映。又稱Latin-1(拉丁編碼)或“西歐語言”吴叶。ASCII碼是包含的僅僅是英文字母,并且沒有完全占滿256個編碼位置序臂,所以它以ASCII為基礎(chǔ)蚌卤,在空置的0xA0-0xFF的范圍內(nèi),加入192個字母及符號,藉以供使用變音符號的拉丁字母語言使用造寝。從而支持德文磕洪,法文等。因而它依然是一個單字節(jié)編碼诫龙,只是比ASCII更全面析显。一個字節(jié)
    • GB2312 : 中國的中文編碼表
    • GBK : 中國的中文編碼表升級,融合了更多的中文文字符號签赃。兩個字節(jié)包含了英文字符和擴展的中文 ISO8859-1+中文字符
    • Unicode : 國際標(biāo)準(zhǔn)碼谷异,融合了多種文字。所有文字都用兩個字節(jié)來表示锦聊,Java語言使用的就是unicode歹嘹。
    • UTF-8 : 最多用三個字節(jié)來表示一個字符(我們以后接觸最多的是iso8859-1、gbk孔庭、utf-8)是1~3個字節(jié)不等長尺上。英文存的是1個字節(jié),中文存的是3個字節(jié)圆到,是為了節(jié)省空間怎抛。
    • 查看上述碼表后,很顯然中文的‘中’在iso8859-1中是沒有對映的編碼的芽淡÷砭或者一個字符在2中碼表中對應(yīng)的編碼不同,例如有一些字在不同的編碼中是有交集的挣菲,例如bjg5 和gbk 中的漢字簡體和繁體可能是一樣的富稻,就是有交集,但是在各自碼表中的數(shù)字不一樣白胀。
      • 例如 : 使用gbk 將中文保存在計算機中椭赋,中 國 對映 100 200 如果使用big5 打開可能? ... 不同的編碼對映的是不一樣的。很顯然纹笼,我們使用什么樣的編碼寫數(shù)據(jù)纹份,就需要使用什么樣的編碼來對數(shù)據(jù)苟跪。
  • 編碼 : 把看得懂的字符變成看不懂碼值這個過程我們稱作為編碼 (字符串 --> 字節(jié)數(shù)組)

    • String類的 getBytes() 方法進行編碼廷痘,將字符串,轉(zhuǎn)為對映的二進制件已,并且這個方法可以指定編碼表笋额。如果沒有指定碼表,該方法會使用操作系統(tǒng)默認碼表篷扩。
    • 注意:中國大陸的Windows系統(tǒng)上默認的編碼一般為GBK兄猩。在Java程序中可以使用System.getProperty("file.encoding")方式得到當(dāng)前的默認編碼
  • 解碼: 把碼值查找對應(yīng)的字符,我們把這個過程稱作為解碼 (字節(jié)數(shù)組 -> 字符串)

    • String類的構(gòu)造函數(shù)完成解碼
      • String(byte[] bytes) : 使用系統(tǒng)默認碼表
      • String(byte[],charset) : 指定碼表
    • 注意:我們使用什么字符集(碼表)進行編碼,就應(yīng)該使用什么字符集進行解碼枢冤,否則很有可能出現(xiàn)亂碼(兼容字符集不會)

注意: 以后編碼與解碼一般我們都使用統(tǒng)一的碼表鸠姨。否則非常容易出亂碼

public class Demo {
    
    public static void main(String[] args) throws Exception {
        /*
        String str = "中國";
        byte[] buf = str.getBytes("utf-8");// 平臺默認的編碼表是gbk編碼表。  編碼
        System.out.println("數(shù)組的元素:"+Arrays.toString(buf)); //
        
        str = new String(buf,"utf-8");  //默認使用了gbk碼表去解碼淹真。 
        System.out.println("解碼后的字符串:"+ str);        
        */
        
        /*String str = "a中國"; // ,0, 97, 78, 45, 86, -3
        byte[] buf = str.getBytes("unicode");  //編碼與解碼的時候指定 的碼表是unicode實際上就是用了utf-16.
        System.out.println("數(shù)組的內(nèi)容:"+ Arrays.toString(buf));
        */
        
        String str = "大家好";
        byte[] buf = str.getBytes(); //使用gbk進行編碼
        System.out.println("字節(jié)數(shù)組:"+ Arrays.toString(buf));  // -76, -13, -68, -46, -70, -61
        
        str = new String(buf,"iso8859-1");
        // 出現(xiàn)亂碼之后都可以被還原嗎讶迁? 
        
        byte[] buf2 = str.getBytes("iso8859-1");
        str = new String(buf2,"gbk"); 

        System.out.println(str);
    }
}
  • 存文件時可以使用各種編碼,但是解碼的時候要對映的采用相同的解碼方式核蘸。我們的字符流自動的做了編碼和解碼的工作巍糯,寫一個中文,字符流進行了編碼客扎,存到了計算機中讀到了一個字符祟峦,字符流進行了解碼,我們可以看到字符徙鱼。因為文件存的都是二進制宅楞。但是拷貝圖片時,是純二進制袱吆,不是有意義的字符咱筛,所以碼表無法轉(zhuǎn)換。
  • 字符流的弊端:
    1. 無法拷貝圖片和視頻杆故。
    2. 拷貝文件使用字節(jié)流而不使用字符流迅箩,因為字符流讀文件涉及到解碼,會先解碼处铛,寫文件的時候又涉及到編碼饲趋,這些操作多余,而且讀和寫的碼表不對應(yīng)還容易引發(fā)問題撤蟆。
      例如FileReader 讀文件奕塑,我們沒有指定編碼時,默認是按照系統(tǒng)編碼gbk進行操作家肯,如果讀到utf-8的文件也是按照gbk編碼進行解碼龄砰,那就會出現(xiàn)問題。

字節(jié)流讀取中文

public class TestIo {
    public static void main(String[] args) throws IOException {
        readFileByInputStream2("c:\\a.txt");
    }
    private static void readFileByInputStream2(String path) throws IOException {
        FileInputStream fis = new FileInputStream(path);
        int len = 0;

        while ((len = fis.read()) != -1) {
            System.out.print((char) len);
        }

    }
}
  • 這個方法讀取文本文件讨衣,中文是無法正確顯示的换棚。很顯然這些字節(jié)需要解碼,可以將字節(jié)輸入流讀取的信息保存在字節(jié)數(shù)組中反镇,指定對應(yīng)的碼表進行解碼即可.
public class TestIo {
    public static void main(String[] args) throws IOException {
        readFileByInputStream("c:\\a.txt");
    }
    private static void readFileByInputStream(String path) throws IOException {
        FileInputStream fis = new FileInputStream(path);
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = fis.read(buffer)) != -1) {
            System.out.println(new String(buffer, 0, len, "gbk"));
        }

    }
}
  • 注意:如果指定的編碼表和解碼表不對應(yīng)就會出現(xiàn)問題
public class TestIo {
    public static void main(String[] args) throws IOException {
        // 該文件默認是gbk編碼
        readFileByInputStream("c:\\a.txt");
    }
    private static void readFileByInputStream(String path) throws IOException {
        FileInputStream fis = new FileInputStream(path);
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = fis.read(buffer)) != -1) {
            // 使用utf-8 解碼固蚤,解錯。
            System.out.println(new String(buffer, 0, len, "utf-8"));
        }
    }
}

字節(jié)流寫出中文

需要編碼歹茶,可以指定碼表夕玩。就需要自己把字符串進行編碼操作后你弦,把得到的二進制內(nèi)容通過字節(jié)流寫入到文件中, 使用String的getBytes方法,無參數(shù)的會使用系統(tǒng)默認的碼表進行編碼燎孟,也可以指定碼表

  • 系統(tǒng)默認編碼
public class TestIo {
    public static void main(String[] args) throws IOException {

        String path = "c:\\test.txt";
        writeFileByOutputStream(path, "世界你好");
        readFileByInputStream(path);
    }
    private static void writeFileByOutputStream(String path, String content)
            throws IOException {
        FileOutputStream fos = new FileOutputStream(path);

        // 把字符串進行編碼操作禽作,系統(tǒng)默認編碼
        byte[] bytes = content.getBytes();
        // 內(nèi)容通過字節(jié)流寫入到文件中。
        fos.write(bytes);
        fos.close();
    }
    private static void readFileByInputStream(String path) throws IOException {
        FileInputStream fis = new FileInputStream(path);
        int len = 0;
        byte[] buffer = new byte[1024];

        while ((len = fis.read(buffer)) != -1) {
            // 二進制解碼揩页,使用系統(tǒng)默認編碼
            System.out.println(new String(buffer, 0, len));
        }
    }
}
  • 使用utf-8進行編碼
public class TestIo {
    public static void main(String[] args) throws IOException {

        String path = "c:\\test.txt";
        writeFileByOutputStream(path, "世界你好");
        readFileByInputStream(path);
    }
    private static void writeFileByOutputStream(String path, String content)
            throws IOException {
        FileOutputStream fos = new FileOutputStream(path);

        // 把字符串進行編碼操作
        byte[] bytes = content.getBytes("utf-8");
        // 內(nèi)容通過字節(jié)流寫入到文件中领迈。
        fos.write(bytes);
        fos.close();
    }
    private static void readFileByInputStream(String path) throws IOException {
        FileInputStream fis = new FileInputStream(path);
        int len = 0;
        byte[] buffer = new byte[1024];

        while ((len = fis.read(buffer)) != -1) {
            // 二進制解碼,使用系統(tǒng)默認編碼
            System.out.println(new String(buffer, 0, len,"utf-8"));
        }
    }
}

在明白了字節(jié)流也可以正確的處理中文字符之后碍沐,就應(yīng)該明白字符流其實就是字節(jié)流在加上系統(tǒng)默認的碼表狸捅。自動進行了編碼和解碼的操作。底層還是使用字節(jié)流讀取文件累提。通過轉(zhuǎn)換流的學(xué)習(xí)就可以明白這些道理尘喝。

轉(zhuǎn)換流

  • InputStreamReader : 查看API文檔,發(fā)現(xiàn)是字節(jié)流通向字符流的橋梁斋陪。查看構(gòu)造朽褪,可以傳遞字節(jié)流,可以指定編碼无虚,該流可以實現(xiàn)什么功能缔赠?很顯然可以包裝我們的字節(jié)流,自動的完成節(jié)流編碼和解碼的工作友题。該流是一個Reader的子類嗤堰,是字符流的體系。所以將轉(zhuǎn)換流稱之為字節(jié)流和字符流之間的橋梁度宦。InputStreamReader 是字節(jié)流通向字符流的橋梁
  • 測試InputStreamReader:
    1. 需要專門新建以GBK編碼的文本文件踢匣。為了便于標(biāo)識,我們命名為gbk.txt
      和以UFT-8編碼的文本文件,命名為utf.txt

    2. 分別寫入漢字”中國”

    3. 編寫測試方法,用InputStreamReader 分別使用系統(tǒng)默認編碼,GBK,UTF-8編碼讀取文件.

public class Demo4 {
    public static void main(String[] args) throws IOException {
        File file = new File("c:\\a.txt");
        File fileGBK = new File("c:\\gbk.txt");
        File fileUTF = new File("c:\\utf.txt");
        // 默認編碼
        testReadFile(file);
        // 傳入gbk編碼文件,使用gbk解碼
        testReadFile(fileGBK, "gbk");
        // 傳入utf-8文件,使用utf-8解碼
        testReadFile(fileUTF, "utf-8");
    }
    // 該方法中nputStreamReader使用系統(tǒng)默認編碼讀取文件.
    private static void testReadFile(File file) throws 
            IOException {
        FileInputStream fis = new FileInputStream(file);
        InputStreamReader ins = new InputStreamReader(fis);
        int len = 0;
        while ((len = ins.read()) != -1) {
            System.out.print((char) len);
        }
        ins.close();
        fis.close();
    }
    // 該方法使用指定編碼讀取文件
    private static void testReadFile(File file, String encod)
            throws IOException {
        FileInputStream fis = new FileInputStream(file);
        InputStreamReader ins = new InputStreamReader(fis, encod);
        int len = 0;
        while ((len = ins.read()) != -1) {
            System.out.print((char) len);
        }
        ins.close();
        }
}

注意:碼表不對應(yīng)
分別測試:
使用系統(tǒng)默認編碼讀取utf-8編碼文件
使用utf-8編碼讀取gbk編碼文件
使用"gbk”編碼讀取utf-8文件.
發(fā)現(xiàn)都會出現(xiàn)亂碼的問題.

// 使用系統(tǒng)默認編碼讀取utf-8
        testReadFile(fileUTF);
        // 傳入gbk編碼文件,使用utf-8解碼
        testReadFile(fileGBK, "utf-8");
        // 傳入utf-8文件,使用"gbk解碼
        testReadFile(fileUTF, "gbk"); 
  • 類 OutputStreamWriter
    OutputStreamWriter
    有了InputStreamReader 可以轉(zhuǎn)換InputStream
    那么其實還有OutputStreamWriter 可以轉(zhuǎn)換OutputStream
    OutputStreamWriter 是字符流通向字節(jié)流的橋梁
    測試OutputStreamWriter
    一: 分別使用OutputStreamWriter使用系統(tǒng)默認編碼,GBK,UTF-8相對應(yīng)的默認編碼文件,GBK編碼文件,UTF-8編碼文件中寫出漢字”中國”.
    二: 在使用上述案例中的readFile方法傳入相對應(yīng)碼表讀取.
public class TestIo {
    public class Demo4 {
    public static void main(String[] args) throws IOException {
        File file = new File("c:\\a.txt");
        File fileGBK = new File("c:\\gbk.txt");
        File fileUTF = new File("c:\\utf.txt");

        // 寫入
        // 使用系統(tǒng)默認碼表寫入
        testWriteFile(file);
        // 使用gbk編碼向gbk文件寫入信息
        testWriteFile(fileGBK, "gbk");
        // 使用utf-8向utf-8文件中寫入信息
        testWriteFile(fileUTF, "utf-8");
    
        // 讀取
        // 默認編碼
        testReadFile(file);
        // 傳入gbk編碼文件,使用gbk解碼
        testReadFile(fileGBK, "gbk");
        // 傳入utf-8文件,使用utf-8解碼
        testReadFile(fileUTF, "utf-8");

    }

    // 使用系統(tǒng)碼表將信息寫入到文件中
    private static void testWriteFile(File file) throws IOException {
        FileOutputStream fos = new FileOutputStream(file);
        OutputStreamWriter ops = new OutputStreamWriter(fos);
        ops.write("中國");
        ops.close();
    }

    // 使用指定碼表,將信息寫入到文件中
    private static void testWriteFile(File file, String encod)
            throws IOException {
        FileOutputStream fos = new FileOutputStream(file);
        OutputStreamWriter ops = new OutputStreamWriter(fos, encod);
        ops.write("中國");
        ops.close();
    }

    // 該方法中nputStreamReader使用系統(tǒng)默認編碼讀取文件.
    private static void testReadFile(File file) throws IOException {
        FileInputStream fis = new FileInputStream(file);
        InputStreamReader ins = new InputStreamReader(fis);
        int len = 0;
        while ((len = ins.read()) != -1) {
            System.out.print((char) len);
        }
        ins.close();
    
    }

    // 該方法適合用指定編碼讀取文件
    private static void testReadFile(File file, String encod)
            throws IOException {
        FileInputStream fis = new FileInputStream(file);
        InputStreamReader ins = new InputStreamReader(fis, encod);
        int len = 0;
        while ((len = ins.read()) != -1) {
            System.out.print((char) len);
        }
    
        ins.close();
    }
}

注意: 碼表不對應(yīng)的問題
分別測試:
向GBK文件中寫入utf-8編碼的信息
向utf文件中寫入gbk編碼的信息
發(fā)現(xiàn)文件都有問題,無法正常的讀取了.

public static void main(String[] args) throws IOException {
        File file = new File("c:\\a.txt");
        File fileGBK = new File("c:\\gbk.txt");
        File fileUTF = new File("c:\\utf.txt");

        // 寫入
        // // 使用系統(tǒng)默認碼表寫入
        // testWriteFile(file);
        // // 使用gbk編碼向gbk文件寫入信息
        // testWriteFile(fileGBK, "gbk");
        // // 使用utf-8向utf-8文件中寫入信息
        // testWriteFile(fileUTF, "utf-8");

        testWriteFile(fileGBK);
        // 向GBK文件中寫入utf-8編碼的信息
        testWriteFile(fileGBK, "utf-8");
        // 向utf文件中寫入gbk編碼的信息
        testWriteFile(fileUTF, "gbk");

        // 讀取
        // 默認編碼
        testReadFile(file);
        // 傳入gbk編碼文件,使用gbk解碼
        testReadFile(fileGBK, "gbk");
        // 傳入utf-8文件,使用utf-8解碼
        testReadFile(fileUTF, "utf-8");

    }

InputStreamReader:字節(jié)到字符的橋梁戈抄。
OutputStreamWriter:字符到字節(jié)的橋梁离唬。

它們有轉(zhuǎn)換作用,而本身又是字符流划鸽。所以在構(gòu)造的時候输莺,需要傳入字節(jié)流對象進來。
構(gòu)造函數(shù):
InputStreamReader(InputStream)
通過該構(gòu)造函數(shù)初始化裸诽,使用的是本系統(tǒng)默認的編碼表GBK嫂用。
InputStreamReader(InputStream,String charSet)
通過該構(gòu)造函數(shù)初始化,可以指定編碼表崭捍。
OutputStreamWriter(OutputStream)
通過該構(gòu)造函數(shù)初始化尸折,使用的是本系統(tǒng)默認的編碼表GBK。
OutputStreamWriter(OutputStream,String charSet)
通過該構(gòu)造函數(shù)初始化殷蛇,可以指定編碼表实夹。
注意:
操作文件的字符流對象是轉(zhuǎn)換流的子類。

注意:
在使用FileReader操作文本數(shù)據(jù)時粒梦,該對象使用的是默認的編碼表亮航。
如果要使用指定編碼表時,必須使用轉(zhuǎn)換流匀们。

如果系統(tǒng)默認編碼是GBK的:
FileReader fr = new FileReader("a.txt");//操作a.txt的中的數(shù)據(jù)使用的本系統(tǒng)默認的GBK缴淋。
操作a.txt中的數(shù)據(jù)使用的也是本系統(tǒng)默認的GBK。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));
這兩句的代碼的意義相同泄朴。
但是:如果a.txt中的文件中的字符數(shù)據(jù)是通過utf-8的形式編碼重抖。使用FileReader就無能為力,那么在讀取時祖灰,就必須指定編碼表钟沛。那么轉(zhuǎn)換流必須使用。
InputStreamReader isr =
new InputStreamReader(new FileInputStream("a.txt"),"utf-8");

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末局扶,一起剝皮案震驚了整個濱河市恨统,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌三妈,老刑警劉巖畜埋,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異畴蒲,居然都是意外死亡悠鞍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門模燥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狞玛,“玉大人,你說我怎么就攤上這事涧窒⌒姆荆” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵纠吴,是天一觀的道長硬鞍。 經(jīng)常有香客問我,道長戴已,這世上最難降的妖魔是什么固该? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮糖儡,結(jié)果婚禮上伐坏,老公的妹妹穿的比我還像新娘。我一直安慰自己握联,他們只是感情好桦沉,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布每瞒。 她就那樣靜靜地躺著,像睡著了一般纯露。 火紅的嫁衣襯著肌膚如雪剿骨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天埠褪,我揣著相機與錄音浓利,去河邊找鬼。 笑死钞速,一個胖子當(dāng)著我的面吹牛贷掖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渴语,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼苹威,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了遵班?” 一聲冷哼從身側(cè)響起屠升,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狭郑,沒想到半個月后腹暖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡翰萨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年脏答,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亩鬼。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡殖告,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雳锋,到底是詐尸還是另有隱情黄绩,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布玷过,位于F島的核電站爽丹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辛蚊。R本人自食惡果不足惜粤蝎,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望袋马。 院中可真熱鬧初澎,春花似錦、人聲如沸虑凛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至墓懂,卻和暖如春焰宣,著一層夾襖步出監(jiān)牢的瞬間霉囚,已是汗流浹背捕仔。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盈罐,地道東北人榜跌。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像盅粪,于是被迫代替她去往敵國和親钓葫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容