java 中GBK 与UTF-8编码的转换
⽂章⽬录
java编码中常遇到的编码转换问题,主要是UTF-8、unicode与GBK编码之间的转换。
经常需要转换的主要原因是:中⽂编码的问题,如果编不对应,经常遇到令⼈烦躁的乱码问题。究其原因是:在unicode系列编码和GBK系列编码中,同⼀个中⽂的字符对应的编码不同。
在java中字符默认是编码的⽅式存储。
java 源⽂件中中⽂字符的编码的问题
windows系统默认的编码为:gbk.命令⾏编译java代码不⽤-encoding 指定编码选项时,会默认按照GBK编码编译,如果源⽂件编码不是GBK编码,编译(可能)将产⽣错误:xxx编码不可映射的字符集。
linux/unix系统默认的编码为:utf-8.命令⾏编译java代码不⽤-encoding 指定编码选项时,会默认按照utf-8编码编译,如果源⽂件编码不是utf-8编码,编译(可能)将产⽣错误:xxx编码不可映射的字符集。
针对以上问题,可在编译时指定和源⽂件⼀致的编码即可,输出中⽂将正常,将不受操作系统默认的编码的影响。
eg: windows下utf-8编码的Test.java代码,利⽤下⾯的编译指令可通过编译,且中⽂在终端输出正常。
UTF-8和GBK 格式的⽂件相互转换
UTF-8和GBK格式的数据不能直接转换,需要先转化为unicode编码,再进⾏转换。
unicode的码表官⽹:
unicode的编码范围和各国语⾔编码映射:
中⽂的编码在unicode码表中的CJK(CJK 是中⽂(Chinese)、⽇⽂(Japanese)、韩⽂(Korean)三国⽂字的缩写)中说明。。特别说明: unicode 和 gbk系列编码之间没有确定的算数关系,如果需要准确的转换,必须通过unicode码表和中⽂字符编码的码表进⾏转换。
⼀般情况下:⾼级的编程语⾔中都会提供 unicode 和 gbk系列编码之间转换的API,像C/C++中如果未提供,可以采⽤第三⽅库的API进⾏转换,没必要浪费时间,⾃⼰造轮⼦。
java 实现⽂件编码的转换javac Test .java -encoding utf -8java Test
1
2
3
4
5public static int convertFileEncoding (String srcFilePath ,String srcCharset , String destFilePath ,String destCharset ,boolean isDeleteSrc ) throws IOException { if (srcFilePath == null || srcFilePath .length () == 0) throw new IllegalArgumentException ("srcFilePath is empty."); if (destFilePath == null || destFilePath .length () == 0) throw new IllegalArgumentException ("destFilePath is empty."); if (srcFilePath .equalsIgnoreCase (destFilePath ))
1
2
3
4
5
6
7
8
throw new IllegalArgumentException ("srcFilePath is the same as destFilePath"); if (srcCharset == null || srcCharset .length () == 0) throw new IllegalArgumentException ("srcCharset is empty."); if (destCharset == null || destCharset .length () == 0) throw new IllegalArgumentException ("destCharset is empty."); if (srcCharset .equalsIgnoreCase (destCharset )) // 编码相同,⽆需转换 return 0; File srcFile = new File (srcFilePath ); FileInputStream fis = null ; InputStreamReader isr = null ; BufferedReader br = null ; FileOutputStream fos = null ; OutputStreamWriter osw = null ; try { fis = new FileInputStream (srcFile ); isr = new InputStreamReader (fis , srcCharset ); // BufferedReader 中defaultCharBufferSize = 8192. // 即:8192 × 2 byte = 16k // 若是utf-8,中⽂占3个字节,16K / 3 = 5461,即只要每⾏中⽂字符数 < 5461,读取的⾏数就是准确的, // 否则,可能会截断⼀⾏,多写⼊'\n',但这种情况⼀般不存在。 // 如果源⽂件中最后⼀⾏没有换⾏符,转码后的⽂件最后会多写⼊⼀个换⾏符 br = new BufferedReader (isr ); // 以UTF-8格式写⼊⽂件,AbsolutePath()即该⽂件的绝对路径,false 代表不追加直接覆盖,true 代表追加⽂件 fos = new
FileOutputStream (destFilePath , false ); osw = new OutputStreamWriter (fos , destCharset ); String str = null ; // 创建StringBuffer 字符串缓存区 StringBuffer sb = new StringBuffer (); int lines = 0; // 通过readLine()⽅法遍历读取⽂件 while ((str = br .readLine ()) != null ) { // 使⽤readLine()⽅法⽆法进⾏换⾏,需要⼿动在原本输出的字符串后⾯加"\n"或"\r" sb .append (str ).append ('\n'); osw .write (sb .toString ()); osw .flush (); lines ++; } if (isDeleteSrc ) { if (srcFile .delete ()) System .out .println (srcFile .getAbsolutePath () + " file is already deleted."); else System .out .println (srcFile .getAbsolutePath () + " file delete fail."); } // System.out.println(lines); return lines ; } catch (FileNotFoundException e ) { // TODO Auto-generated catch block // e.printStackTrace(); throw e ; } catch (UnsupportedEncodingException e ) { // TODO Auto-generated catch block
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
java语言使用的字符码集是73
74
java 不同编码的字节数组的转换
定义class FileUtil实现 throw e ; } catch (IOException e ) { // TODO: handle exception throw e ; } finally { // 与同⼀个⽂件关联的所有输出流(输⼊流),只需关闭⼀个即可 if (null != fis ) try { fis .close (); fis = null ; } catch (IOException e ) { // TODO Auto-generated catch block e .printStackTrace (); } if (null != fos ) try { fos .close (); fos = null ; } catch (IOException e ) { // TODO Auto-generated catch block e .printStackTrace (); } }}
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99public class EncodingUtil { /** * 将原正确编码的字符串src ,转化为编码为srcCharset 的字符串 * * 前提是:确保原字符串的编码是⽆损(完整的). ⽆需知道原字符串的具体编码, * 转化为⽬标编码的字符串由java 库⾃动实现,⽆需⾃⼰⼿动实现。 * * 如果原编码字符串不能转化为⽬标编码,将会抛出UnsupportedEncodingException * * @param src * @param srcCharset * @param destCharet * @return 转换后的字符串 * @throws UnsupportedEncodingException */ public static String convertEncoding_Str (String src ,String srcCharset ,String destCharet ) throws UnsupportedEncodingException { byte [] bts = src .getBytes (destCharet ); return new String (bts , destCharet ); } /** * 将编码为srcCharset 的字节数组src 转化为编码为destCharet 的字节数组 * * @param src * @param srcCharset * @param destCharet * @return * @throws UnsupportedEncodingException */ public static byte [] convertEncoding_ByteArr (byte [] src ,String srcCharset ,String destCharet )
throws UnsupportedEncodingException {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
特别说明: java API中编码正确的字符串可通过String对象的 getBytes(String charset)获得不同编码的字节数组,但是通过字节数组构造字符串对象时,String(byte[],charset) 字节数组的原编码必须和构造字符串对象时指定的编码相同,否则可能构造的并⾮是预期的字符串。
测试程序: (操作系统为:ubuntu16.04)
throws UnsupportedEncodingException { String s = new String (src , srcCharset ); return s .getBytes (destCharet ); } /** * 将字节数组byteArr 转化为2位16进制字符串,每个16进制之间⽤空格分割 * * @param byteArr * @return */ public static String byteToHex (byte ... byteArr ) { if (null == byteArr || byteArr .length == 0) return ""; StringBuffer sb = new StringBuffer (); String tmp = null ; for (byte b : byteArr ) { tmp = Integer .toHexString (b ); // 切记:byte 进⾏运算时,会⾃动转化为int ,否则可能会出错 if (b >>> 31 == 1) { // 最⾼位为1,负数 sb .append (tmp .substring (6)); } else { // 最⾼位为0,正数 if (tmp .length () < 2) sb .append ('0'); sb .append (tmp ); } sb .append (' '); } sb .deleteCharAt (sb .length () - 1); // delete last space return sb .toString (); }}
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72import java .io .IOException ;import java .nio .charset .Charset ;public class Test {// ⼀// GB2312编码:D2BB BIG5编码:A440 GBK 编码:D2BB GB18030编码:D2BB Unicode 编码:4E00 ,utf-8: E4B880// 丁// GB2312编码:B6A1 BIG5编码:A442 GBK 编码:B6A1 GB18030编码:B6A1 Unicode 编码:4E01 ,utf-8: E4B881// 丂// GB2312编码:没有 BIG5编码:没有 GBK 编码:8140 GB18030编码:8140 Unicode 编码:4E02 ,utf-8: E4B882// 七// GB2312编码:C6DF BIG5编码:A443 GBK 编码:C6DF GB18030编码:C6DF Unicode 编码:4E03 ,utf-8: E4B883// \r\n : 0A 0D // 0-9 : 30 39// A-Z : 41~5A a-z : 61~7A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输出// ⼀18丁a 丂七40// GBK : D2BB 3138 B6A1 61 8140 C6DF 3430// unicode : 4E00 3138 4E01 61 4E02 4E03 3430// utf-8 : E4B880 3138 E4B881 61 E4B882 E4B883 3430 public static void main (String [] args ) throws IOException { System .out .println ("start"); String s = "⼀18丁a 丂七40"; System .out .println ("系统默认编码: " + System .getProperty ("ding"));// 查询结果GBK // 系统默认字符编码 System .out .println ("系统默认字符编码:" + Charset .defaultCharset ()); // 查询结果GBK // 操作系统⽤户使⽤的语⾔ System .out .println ("系统默认语⾔:" + System .getProperty ("user.language")); // 查询结果zh // 使⽤系统默认的字符编码 byte [] defaultCharsetArr = s .getBytes (); showByteArr (defaultCharsetArr ,"defaultCharsetArr"); // unicode 编码,在java 所有字符(中英⽂)均占2个字节 byte [] unicodeArr = s .getBytes ("unicode"); showByteArr (unicodeArr ,"unicodeArr"); // gbk 中⽂占2个字节,UTF-8k 中⽂占3个字节; 英⽂字符2者均占1个字节 byte [] gbkArr = s .getBytes ("gbk"); showByteArr (gbkArr ,"gbkArr"); byte [] utf8Arr = s .getBytes ("utf-8"); showByteArr (utf8Arr ,"utf8Arr"); // ISO-8859-1编码中不能出现中⽂,因为其将每个中⽂字符编码为1个字节(⾮法) // 会造成字节的丢失 byte [] isoArr = s .getBytes ("ISO-8859-1"); showByteArr (isoArr ,"isoArr"); // gbk to utf-8 showByteArr (EncodingUtil .convertEncoding_ByteArr (gbkArr ,"gbk","utf-8"),"gbk to utf-8"); showByteArr (EncodingUtil .convert
Encoding_ByteArr (utf8Arr ,"utf-8","gbk"),"utf-8 to gbk"); showByteArr (EncodingUtil .convertEncoding_ByteArr (utf8Arr ,"utf-8","unicode"),"utf-8 to unicode"); System .out .println ("end"); } private static void showByteArr (byte [] arr ,String msg ) { // TODO Auto-generated method stub System .out .println (msg ); System .out .println ("byte[] len=:" + arr .length + "\n" + EncodingUtil .byteToHex (arr )); }}18192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论