編碼與解碼
-
碼表: 常見的碼表如下:
- 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ù)苟跪。
- 例如 : 使用gbk 將中文保存在計算機中椭赋,
-
編碼 : 把看得懂的字符變成看不懂碼值這個過程我們稱作為編碼 (字符串 --> 字節(jié)數(shù)組)
- String類的
getBytes()
方法進行編碼廷痘,將字符串,轉(zhuǎn)為對映的二進制件已,并且這個方法可以指定編碼表笋额。如果沒有指定碼表,該方法會使用操作系統(tǒng)默認碼表篷扩。 - 注意:中國大陸的Windows系統(tǒng)上默認的編碼一般為GBK兄猩。在Java程序中可以使用System.getProperty("file.encoding")方式得到當(dāng)前的默認編碼
- String類的
-
解碼: 把碼值查找對應(yīng)的字符,我們把這個過程稱作為解碼 (字節(jié)數(shù)組 -> 字符串)
- String類的構(gòu)造函數(shù)完成解碼
-
String(byte[] bytes)
: 使用系統(tǒng)默認碼表 -
String(byte[],charset)
: 指定碼表
-
- 注意:我們使用什么字符集(碼表)進行編碼,就應(yīng)該使用什么字符集進行解碼枢冤,否則很有可能出現(xiàn)亂碼(兼容字符集不會)
- String類的構(gòu)造函數(shù)完成解碼
注意: 以后編碼與解碼一般我們都使用統(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)換。
- 字符流的弊端:
- 無法拷貝圖片和視頻杆故。
- 拷貝文件使用字節(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:
需要專門新建以GBK編碼的文本文件踢匣。為了便于標(biāo)識,我們命名為gbk.txt
和以UFT-8編碼的文本文件,命名為utf.txt分別寫入漢字”中國”
編寫測試方法,用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");