首页 > 编程语言 >java是如何编码解码的

java是如何编码解码的

时间:2023-05-29 13:04:23浏览次数:53  
标签:编码 读取 java String 解码 格式 new 字节


编码&解码

在java中主要有四个场景需要进行编码解码操作:

1:I/O操作

2:内存

3:数据库

4:javaWeb

下面主要介绍前面两种场景,数据库部分只要设置正确编码格式就不会有什么问题,javaWeb场景过多需要了解URL、get、POST的编码,servlet的解码,所以javaWeb场景下节LZ介绍。

I/O操作

在前面LZ就提过乱码问题无非就是转码过程中编码格式的不统一产生的,比如编码时采用UTF-8,解码采用GBK,但最根本的原因是字符到字节或者字节到字符的转换出问题了,而这中情况的转换最主要的场景就是I/O操作的时候。当然I/O操作主要包括网络I/O(也就是javaWeb)和磁盘I/O。网络I/O下节介绍。

首先我们先看I/O的编码操作。

java是如何编码解码的_java

InputStream为字节输入流的所有类的超类,Reader为读取字符流的抽象类。java读取文件的方式分为按字节流读取和按字符流读取,其中InputStream、Reader是这两种读取方式的超类。

按字节

我们一般都是使用InputStream.read()方法在数据流中读取字节(read()每次都只读取一个字节,效率非常慢,我们一般都是使用read(byte[])),然后保存在一个byte[]数组中,最后转换为String。在我们读取文件时,读取字节的编码取决于文件所使用的编码格式,而在转换为String过程中也会涉及到编码的问题,如果两者之间的编码格式不同可能会出现问题。例如存在一个问题test.txt编码格式为UTF-8,那么通过字节流读取文件时所获得的数据流编码格式就是UTF-8,而我们在转化成String过程中如果不指定编码格式,则默认使用系统编码格式(GBK)来解码操作,由于两者编码格式不一致,那么在构造String过程肯定会产生乱码,如下:


1. File file = new File("C:\\test.txt");  
2. InputStream input = new FileInputStream(file);  
3. StringBuffer buffer = new StringBuffer();  
4. byte[] bytes = new byte[1024];  
5. for(int n ; (n = input.read(bytes))!=-1 ; ){  
6. new String(bytes,0,n));  
7. }  
8. System.out.println(buffer);


  

输出结果:锘挎垜鏄?cm

test.txt中的内容为:我是 cm。

要想不出现乱码,在构造String过程中指定编码格式,使得编码解码时两者编码格式保持一致即可:




1. buffer.append(new String(bytes,0,n,"UTF-8"));



  

按字符

其实字符流可以看做是一种包装流,它的底层还是采用字节流来读取字节,然后它使用指定的编码方式将读取字节解码为字符。在java中Reader是读取字符流的超类。所以从底层上来看按字节读取文件和按字符读取没什么区别。在读取的时候字符读取每次是读取留个字节,字节流每次读取一个字节。

字节&字符转换

字节转换为字符一定少不了InputStreamReader。API解释如下:InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。 每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。API解释非常清楚,InputStreamReader在底层读取文件时仍然采用字节读取,读取字节后它需要根据一个指定的编码格式来解析为字符,如果没有指定编码格式则采用系统默认编码格式。


    1. String file = "C:\\test.txt";   
    2. "UTF-8";   
    3. // 写字符换转成字节流  
    4. new FileOutputStream(file);   
    5. new OutputStreamWriter(outputStream, charset);   
    6. try {   
    7. "我是 cm");   
    8. finally {   
    9.             writer.close();   
    10.          }   
    11.            
    12. // 读取字节转换成字符  
    13. new FileInputStream(file);   
    14. new InputStreamReader(   
    15.          inputStream, charset);   
    16. new StringBuffer();   
    17. char[] buf = new char[64];   
    18. int count = 0;   
    19. try {   
    20. while ((count = reader.read(buf)) != -1) {   
    21. 0, count);   
    22.             }   
    23. finally {   
    24.             reader.close();   
    25.          }  
    26.          System.out.println(buffer);


      

    内存

    首先我们看下面这段简单的代码


    1. String s = "我是 cm";   
    2. byte[] bytes = s.getBytes();   
    3. String s1 = new String(bytes,"GBK");   
    4. String s2 = new String(bytes);


      

    在这段代码中我们看到了三处编码转换过程(一次编码,两次解码)。先看String.getTytes():


    1. public byte[] getBytes() {  
    2. return StringCoding.encode(value, 0, value.length);  
    3.     }


      

    内部调用StringCoding.encode()方法操作:



     

    1. static byte[] encode(char[] ca, int off, int len) {  
    2.         String csn = Charset.defaultCharset().name();  
    3. try {  
    4. // use charset name encode() variant which provides caching.  
    5. return encode(csn, ca, off, len);  
    6. catch (UnsupportedEncodingException x) {  
    7.             warnUnsupportedCharset(csn);  
    8.         }  
    9. try {  
    10. return encode("ISO-8859-1", ca, off, len);  
    11. catch (UnsupportedEncodingException x) {  
    12. // If this code is hit during VM initialization, MessageUtils is  
    13. // the only way we will be able to get any kind of error message.  
    14. "ISO-8859-1 charset not available: "  
    15.                              + x.toString());  
    16. // If we can not find ISO-8859-1 (a required encoding) then things  
    17. // are seriously wrong with the installation.  
    18.             System.exit(1);  
    19. return null;  
    20.         }  
    21.     }


      

    encode(char[] paramArrayOfChar, int paramInt1, int paramInt2)方法首先调用系统的默认编码格式,如果没有指定编码格式则默认使用ISO-8859-1编码格式进行编码操作,进一步深入如下:


    1. String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;



      

    同样的方法可以看到new String 的构造函数内部是调用StringCoding.decode()方法:


    1. public String(byte bytes[], int offset, int length, Charset charset) {  
    2. if (charset == null)  
    3. throw new NullPointerException("charset");  
    4.         checkBounds(bytes, offset, length);  
    5. this.value =  StringCoding.decode(charset, bytes, offset, length);  
    6.     }


      

    decode方法和encode对编码格式的处理是一样的。

    对于以上两种情况我们只需要设置统一的编码格式一般都不会产生乱码问题。

     

    编码&编码格式

    首先先看看java编码类图[1]

    java是如何编码解码的_System_02

    首先根据指定的chart设置ChartSet类,然后根据ChartSet创建ChartSetEncoder对象,最后再调用 CharsetEncoder.encode 对字符串进行编码,不同的编码类型都会对应到一个类中,实际的编码过程是在这些类中完成的。下面时序图展示详细的编码过程:

    java是如何编码解码的_java_03

    通过这编码的类图和时序图可以了解编码的详细过程。下面将通过一段简单的代码对ISO-8859-1、GBK、UTF-8编码


    1. public class Test02 {  
    2. public static void main(String[] args) throws UnsupportedEncodingException {  
    3. "我是 cm";  
    4.         Test02.printChart(string.toCharArray());  
    5. "ISO-8859-1"));  
    6. "GBK"));  
    7. "UTF-8"));  
    8.     }  
    9.       
    10. /**
    11.      * char转换为16进制
    12.      */  
    13. public static void printChart(char[] chars){  
    14. for(int i = 0 ; i < chars.length ; i++){  
    15. " ");   
    16.         }  
    17. "");  
    18.     }  
    19.       
    20. /**
    21.      * byte转换为16进制
    22.      */  
    23. public static void printChart(byte[] bytes){  
    24. for(int i = 0 ; i < bytes.length ; i++){  
    25. 0xFF);   
    26. if (hex.length() == 1) {   
    27. '0' + hex;   
    28.              }   
    29. " ");   
    30.         }  
    31. "");  
    32.     }  
    33. }  
    34. -------------------------outPut:  
    35. 6211 662f 20 63 6d   
    36. 3F 3F 20 63 6D   
    37. CE D2 CA C7 20 63 6D   
    38. E6 88 91 E6 98 AF 20 63 6D


      

    通过程序我们可以看到“我是 cm”的结果为:

    char[]:6211 662f 20 63 6d

    ISO-8859-1:3F 3F 20 63 6D 
    GBK:CE D2 CA C7 20 63 6D 
    UTF-8:E6 88 91 E6 98 AF 20 63 6D

    图如下:

    java是如何编码解码的_数据库_04

    标签:编码,读取,java,String,解码,格式,new,字节
    From: https://blog.51cto.com/u_16131764/6370106

    相关文章

    • Java中的Copy-On-Write容器
      Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,......
    • java线程池
      1.JDK中的Executor框架是基于生产者-消费者模式的线程池,提交任务的线程是生产者,执行任务的线程是消费者。Executor线程池可以用于异步任务执行,而且支持很多不同类型任务执行策略,同时为任务提交和任务执行之间的解耦提供了标准方法。Executor线程池支持如下三种线程执行策略:(1).顺序......
    • javascript常用正则表达式
      javascript身份证号验证正则1.//这个可以验证15位和18位的身份证,并且包含生日和校验位的验证。2.//如果有兴趣,还可以加上身份证所在地的验证,就是前6位有些数字合法有些数字不合法。3.4.function5.num=num.toUpperCase();6.//身份证号码为15位或者18......
    • Javascript编程风格
       所谓"编程风格"(programmingstyle),指的是编写代码的样式规则。不同的程序员,往往有不同的编程风格。 有人说,编译器的规范叫做"语法规则"(grammar),这是程序员必须遵守的;而编译器忽略的部分,就叫"编程风格"(programmingstyle),这是程序员可以自由选择的。这种说法不完全正确,程序员固然可......
    • java中fail-fast 和 fail-safe的区别
       在我们详细讨论这两种机制的区别之前,首先得先了解并发修改。1.什么是并发修改?当一个或多个线程正在遍历一个集合Collection,此时另一个线程修改了这个集合的内容(添加,删除或者修改)。这就是并发修改。 2.什么是fail-fast机制?fail-fast机制在遍历一个集合时,当集合结构被修改,会抛......
    • java socket
           对于JavaSocket编程而言,有两个概念,一个是ServerSocket,一个是Socket。服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了。首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个......
    • Java中的堆和栈的区别
      当一个人开始学习Java或者其他编程语言的时候,会接触到堆和栈,由于一开始没有明确清晰的说明解释,很多人会产生很多疑问,什么是堆,什么是栈,堆和栈有什么区别?更糟糕的是,Java中存在栈这样一个后进先出(LastInFirstOut)的顺序的数据结构,这就是java.util.Stack。这种情况下,不免让很多人更加......
    • java二进制运算
      //对于原码,反码,补码而言,需要注意以下几点://(1) 二进制的最高位是符号位,0表示正数,1表示负数;//(2) 正数的原码,反码,补码都一样;//(3) 负数的原码=对应正数的二进制原码,最高位设为1;//(4) 负数的反码=它的原码符号位不变,其他位取反;//(5) 负数的补码=它的......
    • 压栈思想计算Java运算表达式
           栈的规则是先进后出。利用压栈的思想来计算四则运算表达式是这样的:我们给定两个栈,一个用来存放数字、一个用来存放对应的操作符。假定我们有一个给定的四则运算表达式a+b+c/d*(e+f)-d*a,那我们先把这个表达式拆分成一个个的数字或者是运算符、或者就是括号了。然后我们......
    • java IO
      packagecom.jaeson.javastudy;importjava.io.*;importjava.util.zip.*;publicclassIOTest{ //跨平台的文件分割符 staticvoidseparator(){ System.out.println(File.separator); //\System.out.println(File.pathSeparator); //; } //创建文件,创......