`
tjuximo
  • 浏览: 50586 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java编码浅析

阅读更多
Java字符串编码浅析
前些天在千手观音项目中,和算法端传参数的时候遇到了编码处理的问题,经过一段时间的学习摸索,对Java字符编码有了一定的了解,现将一篇总结分享给大家,希望大家指正~~

首先,对于Java来说,Java中的字符串都是Unicode编码的,Java的class文件采用的是utf-8的编码方式,JVM在运行时采用的是utf16的编码方式。
这里要说说Unicode编码了,Unicode实际上不是一种编码的实现,只是一种字符集,Unicode字符集又称为UCS(Unicode Character Set),而utf-8是对UCS-2(采用两个字节编码)的实现,utf-16是对UCS-4(采用四个字节编码)的实现。
所以可以这么说,JVM中所有的字符串资源都是Unicode的,也就是说,所有的String类型的数据都是Unicode编码的,网上有种说法就是,既然只有一种编码,那么可以简要的认为,JVM里面的String是不带编码的,String相当于char[]。

虽然所有的String对象都是Unicode的编码的,但是JVM中的byte[]数据是带编码的,比如GBK、UTF8、GB2312等等。
在JVM中,一个GBK编码的byte[]转化成String,实际上的操作是从GBK编码向Unicode编码的转换。一个String要转换成一个GBK编码的byte[],实际上的操作是从Unicode向GBK的转换。那么,一个GBK编码的byte[]要转换成Big5编码的byte[]呢?实际上可以先吧GBK的转成Unicode的,然后再转成Big5的,呵呵~~
所以,在Java中,Unicode是所有编码间转换的中间介质,所有的编码都可以转换到Unicode,而Unicode也可以转换到其他任意一种编码。根据Charset类的方法,我们可以得到,Java支持了160中字符集,这样任意字符集之间的转换,只需要160+160=320个方法就行了,如果是让这些字符集可以两两转换,则需要160*159=25440个方法!!!

那么,我们在什么情况下会遇到编码问题呢?遇到了编码问题该怎么解决呢?
1、 在外部资源(非数据库)读取数据的时候,因为要涉及到针对该外部文件的编码进行解码,所以我们需要使用该文件采用的字符集来读取文件内的数据:
例如:
InputStream is = new FileInputStream(“config/bp.properties”);
InputSteamReader streamReader = new InputStreamReader(is,”GBK”);
这里,我们采用GBK编码来读取config文件夹下的bp.properties中的数据,通过查看streamReader的encoding属性可以得到印证:
assertEquals(“GBK”, streamReader.getEncoding());
正是由于我们对bp.peoperties文件指定了正确的编码,所以在它转换成char数组时才能够正确的对其进行解码(从GBK到Unicode)。
char[] chars = new char[is.available()];
streamReader.reader(chars, 0, is.available());
然而,我们通常都是采用了一下的方式:
InputStream is = new FileInputStream(“config/bp.properties”);
InputSteamReader streamReader = new InputStreamReader(is);
这时候InputSteamReader是采用的JVM的默认字符集读取的外部文件,(如果没有修改过JVM参数,JVM默认字符集在windows下为GBK或GB2312,linux下为utf8) 。
一般来说,如果该文件的编码方式和操作系统使用的字符集一样,那么采用这种默认方式是可以的。但是如果文件编码方式和操作系统使用的字符集(一般和JVM默认字符集一致)不一样呢?那就是乱码了~~
当然,在这里,我们有必要说一下JVM的默认编码。这个默认的编码是在JVM启动时决定的,通常是根据语言环境和底层操作系统的charset来确定。可以通过下面的方法获取JVM的默认字符集:
Charset.defaultCharset();
然而由于这个默认值是“根据语言环境和底层操作系统的charset来确定”的,所以,其中任何一项变了,这个默认值都可能会变。比如说,在Xp下,咱们用eclipse自动new的Java文件都是GBK编码的,这时候Charset.defaultCharset() = “GBK”,但如果把这个Java文件的Text file encoding设为“UTF-8”,那么Charset.defaultCharset() = “UTF-8”,而且,javac命令也可以带上参数,明确指定编译时候的编码,例如:javac –encoding utf-8 Test.java
所以,在处理外部资源文件的时候,我们最好能够根据文件本身的编码对其进行由其编码到Unicode的转码。
其次,与此相似的,在向外部写数据的时候,也要将数据先转换成外部文件的编码才可以。
在千手观音项目中向算法接口post参数流的时候,BP从数据库中获取的中文是GBK编码的,而算法端是以UTF8读的,由于起初编码的时候没有指定流的编码,照成了编码的不一致,导致乱码。该问题通过下面的方式得到解决:
PrintWriter out = null;
URLConnection conn = realUrl.openConnection();
out = new PrintWriter(new OutputStreamWriter (conn.getOutputStream(), encoding));

2、 字符串和字节数组相互转换的时候,我们一般是采用"abc".getBytes()的方式将字符串转换成字节数组,但是这个转换采用的是什么编码呢?
和前面一样,"abc".getBytes(Charset.defaultCharset())具有同样的效果。也就是说,它根据JVM的默认编码(不一定是Unicode噢~)把字符串转换成字节数组。
那么,在由字节数组创建字符串的时候,我们一般默认是new String("abc".getBytes()),这样呢,同样是使用的系统的默认字符集来解码字节数组(例如从GBK到Unicode) 。
所以,我们最好明确指定编码,例如:
"abc".getBytes("GBK");//获取GBK编码的byte[]
new String("abc".getBytes("GBK"));
String类还有一个构造函数是:
    public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException
    {
this(bytes, 0, bytes.length, charsetName);
}
API中对其解释是“通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String”。
但是如果对于new String(a.getBytes("GBK"),"UTF-8");来说呢?有人的解释是“将abc字符串从GBK编码转换成UTF-8编码”,那么这不和之前说的所有的字符串都是Unicode编码冲突了吗?
然而事实是:对于某个字符串,我们本应该用UTF-8来读取并解码字符串,但是结果却采用了GBK的方式,导致生成了一个错误的字符串a,要恢复,只能先把字符串恢复成原始字节数组,然后通过正确的UTF-8的编码将其再次编码成字符串。
分享到:
评论

相关推荐

    Java中文乱码浅析及解决方案

    为了解决这个问题,我们可以统一Java编译器和源文件编码,例如在IDEA中设置项目编码为UTF-8,并在POM.XML中配置UTF-8编码: ```xml <!-- java 源文件编码 --> <project.build.sourceEncoding>UTF-8 ...

    浅析Java开发平台下的汉字编码与解码及应用

    简明扼要的说明清楚了Java对汉字的编码与解码,附有实例代码参考。

    浅析Java 常用的 4 种加密方式(MD5+Base64+SHA+BCrypt)

    Base64 是一种常用的编码方式,可以将二进制数据转换为 ASCII 码。Base64 加密的特点是可以将任意长度的数据转换为一个固定长度的字符串。Java 中可以使用 Base64 类来实现 Base64 加密。 Base64Util 是一个工具类...

    浅析软件工程方法论在Java教学中的应用.pdf

    《浅析软件工程方法论在Java教学中的应用》一文主要探讨了如何将软件工程的理论与实践融入Java教学中,以解决“软件危机”带来的挑战。软件危机是指在软件开发过程中遇到的质量、成本和时间等问题,如高昂的开发成本...

    浅析几种Java播放音频技术及实例.pdf

    Java提供了两种API用于处理音频数据流,一种是针对标准计算机设备的API,它可以完成音频的采集、编码、传输和播放等任务;另一种是适用于嵌入式设备的API,支持语音的识别、合成和传输等功能。在本文中,讨论的重点...

    浅析Java8的函数式编程

    "浅析Java8的函数式编程" Java8的函数式编程是Java语言中的一个重要特性,它允许开发者使用函数式编程的思想来编写代码,从而提高代码的可读性、可维护性和性能。在本文中,我们将深入探讨Java8的函数式编程,了解...

    java与php的区别浅析

    Servlet过滤器,URL编码,安全Web服务都是用于Java,以实现应用程序安全性。而PHP没有这种安全处理的概念。 5、反射概念:Java从第一天开始就在内部具有反射概念,如接口,重复类,抽象类或方法概念。PHP 5.0以前...

    浅析12306售票算法(java版)

    《12306售票算法解析(Java版)》 12306是中国铁路客户服务中心的官方网站,其售票系统采用了一种独特的算法来处理庞大的购票需求。本文将重点探讨12306售票算法的Java实现,以及如何在实际操作中确保公平性和效率...

    Android JNI 浅析

    《Android JNI 浅析》 Android JNI,全称为Java Native Interface,是Java平台的一个核心特性,它提供了一种方式让Java应用程序能够与本地C/C++代码进行交互。JNI使得开发者可以充分利用Java和C/C++的优点,例如...

    浅析Digester

    【标题】:浅析Digester 在Java开发中,我们经常需要处理XML文件,解析XML并将其内容映射到相应的Java对象上。这时,Apache Commons Digester库就派上了用场。本文将深入探讨Digester的功能、工作原理以及如何在...

    浅析Java编程中枚举类型的定义与使用

    Java中的枚举类型是面向对象编程中的一种特殊类型,它用于定义一组有限的、预定义的常量。枚举在Java中通过`enum`关键字来创建,它在很多方面类似于...通过枚举,开发者可以避免硬编码常量,确保代码的一致性和正确性。

    浅析Java IO相关知识点

    总之,Java IO涉及的内容广泛,包括流、缓冲、字符编码、对象序列化、文件操作、网络通信等多个方面。理解和掌握这些知识点对于编写高效、稳定的应用程序至关重要。无论是进行简单的文件读写,还是构建复杂的服务器...

    Netty实现原理浅析

    ### Netty实现原理浅析 #### 一、总体结构 Netty是一款由JBoss推出的高效且功能丰富的Java NIO框架,旨在简化网络编程并提高性能。为了更好地理解Netty的工作原理,我们首先需要了解它的整体架构。Netty的总体结构...

    浅析java移位符的具体使用

    在Java编程语言中,移位运算符是一种特殊且功能强大的工具,允许程序员以一种高效的方式操作二进制数据。...掌握这些知识,将使得Java开发者在进行位级操作时如虎添翼,大幅提升编码的效率和质量。

    浅析java中 Spring MVC 拦截器作用及其实现

    在Java的Spring MVC框架中,拦截器(Interceptor)是一个强大的工具,它允许开发者在请求处理的前后阶段执行自定义逻辑,比如进行权限检查、日志记录、性能统计等。这篇文章将深入探讨Spring MVC拦截器的作用、实现...

    基本数据类型存储格式浅析

    - 在C/C++/Java等语言中,整型分为short、int、long、long long等,它们在不同的平台和编译器下有不同的字节数。通常,short占用2个字节(16位),int占用4个字节(32位),long占用4或8个字节,long long占用8个...

    JSP安全编程实例浅析

    JavaBean是一个遵循特定编码规范的Java对象,它可以被JSP页面或其他Java程序使用。通过在JSP页面中嵌入`<jsp:useBean>`和`<jsp:setProperty>`标签,可以轻松地创建和设置JavaBean的对象属性。 **示例**: ```jsp *...

    浅析Java基于Socket的文件传输案例

    在这个例子中,`sendMessage()`方法使用`DataOutputStream`发送不同平台的标识(如0x1代表Windows,0x2代表Unix,0x3代表Linux)或者UTF编码的字符串。`getMessageStream()`方法返回一个`DataInputStream`实例,以便...

Global site tag (gtag.js) - Google Analytics