java里关于String的编码与解码

java里关于String的编码与解码,第1张

public

byte[]

getBytes(String

charsetName)

使用指定的字符集将此String编码为byte序列,结果存在一个byte数组中

public

String(byte[]

bytes,

String

charsetName)

通过使用指定的

charset

解码指定的

byte

数组,构造一个新的

String。

在网络传输中,信息都是以字节序列的方式传输的。所以,发送方的String要按照某种编码方式(如UTF-8,GBK)编码为字节序列,在网络中传输后,接收方取得这个字节序列,按照相同的编码方式将字节序列解码为String。

请看下面的代码片段:

String

name

=

"张三";

byte[]

b1

=

namegetBytes("UTF-8");

String

name1

=

new

String(b1,

"UTF-8");

//编码解码相同,正常显示

Systemoutprintln(name1);

String

name2

=

new

String(b1,

"GBK");

//编码解码不同,乱码

Systemoutprintln(name2);

byte[]

b2

=

namegetBytes("GBK");

String

name3

=

new

String(b2,

"GBK");

//编码解码相同,正常显示

Systemoutprintln(name3);

String

name4

=

new

String(b2,

"UTF-8");

//编码解码不同,乱码

Systemoutprintln(name4);

至于你的那个情况,要先用gbk编码,然后再用utf-8解码才能获得正常的字符串,我估计是因为

1传输过来的字节码是用utf-8编码的,假设字节码为b。

2你获得的那个字符串,假设为s,是用gbk对b进行解码获得的字符串,所以是乱码。

3你使用gbk对s进行编码,用gbk解码之后再编码,于是获得了原来的b。

4你使用utf-8解码,所以获得了正常的字符串。

简单的说:

b

->

(gbk解码)

->

乱码

->

[此处开始是你做的](gbk编码)

->

b

->

(utf-8解码)

->

正常字符串

研究完编码收获会不小的,对以后理解Java的输入输出(尤其是网络通信和文件读写)都很有帮助。

编码问题存在两个方面:JVM之内和JVM之外。

1、Java文件编译后形成class

这里Java文件的编码可能有多种多样,但Java编译器会自动将这些编码按照Java文件的编码格式正确读取后产生class文件,这里的class文件编码是Unicode编码(具体说是UTF-16编码)。

因此,在Java代码中定义一个字符串:

String s="汉字";

不管在编译前java文件使用何种编码,在编译后成class后,他们都是一样的----Unicode编码表示。

2、JVM中的编码

JVM加载class文件读取时候使用Unicode编码方式正确读取class文件,那么原来定义的String s="汉字";在内存中的表现形式是Unicode编码。

如果是Java的String对象的话,则一定是Unicode的,这个没有为什么,Java就是这么定的。

我猜你的问题应该是如何判断一段字节流是什么编码类型,对吗?比如一个文件,或是网络上面取下来的一段Byte数组,你需要用一个合适的编码来解析成字符串。

这个让你失望了,没有一个文档化的,确定的方法来判断,只能用测试的方法,这个方法也只是猜测,不能百分百的确定,方法如下:

用常见的编码方式对字节流进行解码,比如Unicode,UTF8, UTF8 without BOM, UTF16, ANSI等等。

对解析的结果进行判断,是不是一个合理的可打印字符,可打印字符最多的解码方式就是最可能的编码了。

如何判断可打印字符?流程如下:把解析好的字符串按照字符进行遍历,把每一个字符转化成Unicode编码,看看这些编码是不是Unicode的支持范围极客。

如果发现有种编码方式都是可打印字符,那么再使用本步骤:对字符串进行分词,分词这个在此不作赘述,你自己再研究一下。分词效果好的就是最可能的编码了。(不过通常到第三步就能搞定了,第四部绝大部分用不着)

看这能否帮助你。

import javaioByteArrayOutputStream;

import javaioUTFDataFormatException;

import javautilVector;

public class Utils {

public static String byteArrayToString(byte[] bytes, String encoding) {

char[] map = "\u0402\u0403\u201A\u0453\u201E\u2026\u2020\u2021\u20AC\u2030\u0409\u2039\u040A\u040C\u040B\u040F\u0452\u2018\u2019\u201C\u201D\u2022\u2013\u2014\uFFFD\u2122\u0459\u203A\u045A\u045C\u045B\u045F\u00A0\u040E\u045E\u0408\u00A4\u0490\u00A6\u00A7\u0401\u00A9\u0404\u00AB\u00AC\u00AD\u00AE\u0407\u00B0\u00B1\u0406\u0456\u0491\u00B5\u00B6\u00B7\u0451\u2116\u0454\u00BB\u0458\u0405\u0455\u0457\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F"toCharArray();

if (encodingequals("UTF-8")) {

encoding = "ISO-8859-1";

}

try {

return decodeUTF8(bytes, false);

} catch(UTFDataFormatException udfe) {}

char[] chars = new char[byteslength];

for (int i = 0; i < byteslength; i ) {

byte b = bytes[i];

chars[i] = (b >= 0) (char) b : map[b 128];

}

return new String(chars);

}

public static byte[] encodeUTF8(String text) {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

byte[] ret;

for (int i = 0; i < textlength(); i ) {

char c = textcharAt(i);

if (c != '\u0000' && c < '\u0080') {

baoswrite(c);

} else if (c == '\u0000' || (c >= '\u0080' && c < '\u0800')) {

baoswrite((byte)(0xc0 | (0x1f & (c >> 6))));

baoswrite((byte)(0x80 | (0x3f & c)));

} else {

baoswrite((byte)(0xe0 | (0x0f & (c >> 12))));

baoswrite((byte)(0x80 | (0x3f & (c >> 6))));

baoswrite((byte)(0x80 | (0x3f & c)));

}

}

ret = baostoByteArray();

return ret;

}

private static String decodeUTF8(byte[] data, boolean gracious) throws UTFDataFormatException {

byte a,

b,

c;

StringBuffer ret = new StringBuffer();

for (int i = 0; i < datalength; i ) {

try {

a = data[i];

if ((a & 0x80) == 0) {

retappend((char) a);

} else if ((a & 0xe0) == 0xc0) {

b = data[i 1];

if ((b & 0xc0) == 0x80) {

retappend((char)(((a & 0x1F) << 6) | (b & 0x3F)));

i ;

} else {

throw new UTFDataFormatException("Illegal 2-byte group");

}

} else if ((a & 0xf0) == 0xe0) {

b = data[i 1];

c = data[i 2];

if (((b & 0xc0) == 0x80) && ((c & 0xc0) == 0x80)) {

retappend((char)(((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)));

i = 2;

} else {

throw new UTFDataFormatException("Illegal 3-byte group");

}

} else if (((a & 0xf0) == 0xf0) || ((a & 0xc0) == 0x80)) {

throw new UTFDataFormatException("Illegal first byte of a group");

}

} catch(UTFDataFormatException udfe) {

if (gracious) {

retappend("");

} else {

throw udfe;

}

} catch(ArrayIndexOutOfBoundsException aioobe) {

if (gracious) {

retappend("");

} else {

throw new UTFDataFormatException("Unexpected EOF");

}

}

}

return rettoString();

}

public static String[] splitString(String str, String delim) {

if (str == null) {

return null;

} else if (strequals("") || delim == null || delimlength() == 0) {

return new String[] {

str

};

}

String[] s;

Vector v = new Vector();

int pos,

newpos;

pos = 0;

newpos = strindexOf(delim, pos);

while (newpos != -1) {

vaddElement(strsubstring(pos, newpos));

pos = newpos delimlength();

newpos = strindexOf(delim, pos);

}

vaddElement(strsubstring(pos));

s = new String[vsize()];

for (int i = 0; i < slength; i ) {

s[i] = (String) velementAt(i);

}

return s;

}

}

这种编码问题真是很tricky的问题。说它tricky是因为这至少涉及到以下4种编码选取的排列组合(有时甚至更多),更有时乃至会发生错进错出,负负得正,中间过程错了但反而到不是乱码的情况。

(1)源代码的编码

(2)编译时告诉java编译器的源代码编码

(3)运行时jvm参数fileencoding

(4)输出终端对输出字节流的解码所采用的码组

在这简单情况下(1)和(2)一致,(3)和(4)一致就不会因为编解码映射错误(当然字符向终端字体映射的错误是另一回事,如字体缺失之类)。而(1)(2)和(3)(4)不必一致,这样就使得不必强求开发编译环境和运行应用环境的编码必须一致。

源代码的录入与编译若在在一个平台上时,大多数情况没有问题(反而用聪明的Idea IDE设置错误时会乱套,越是简陋的开发环境越不太会错)。但是如果你在中文GBK编码平台上的源代码在别人的unicode编码平台上编译,就有问题了。所以和别人,特别是和不同母语的人合作编程时,建议要么约定一律用unicode作为源文件编码;要么只用ASCII字符,反正其他编码一般都和ASCII兼容的,对于非ASCII字符,用Java的/uxxxx表示机制,比如"中国"就表示为"\u4e2d\u56fd"。4e2d和56fd分别是中国二字的unicode十六进制编码。

但我认为楼主在这里其实主要关心的是运行时的编码一致问题,即(3)和(4)。所以言归正传,让我们来检查它们是否一致。

由于正如上述,iso8859-1编码集其实是被其他所有公认的编码集所兼容的,也就是说它是所有公认编码集的公共子集。所以以iso8859-1为基础可以外延到任何一个公认编码集。事实上大多数情况也是这样做的。比如java System property里设定了encoding为iso8859-1,事实上不仅仅是一个Latin字母的映射,在非Latin区域按JVM宿主 *** 作系统的编码扩展。即选iso8859-1其实是选择了宿主 *** 作系统的默认编码。

假设楼主的 *** 作系统编码是GBK,那么fileencoding=iso8859-1相当于选择了fileencoding=GBK。那么Systemoutprintln()这个核心类方法会将china字符转换为fileencoding指定的编码(GBK)字节由out流输出给最终out所绑定的终端。比如console一般采用系统默认编码也是GBK的话,那就和fileencoding一致,能正常解码,不会乱码。

至于Systemoutwrite()直接写字节流。由于该字节流是由chinagetBytes()得到的,在不指定编码的时候使用fileencoding指定的默认值的(即GBK),因此Str->Byte的编码方法GBK和console采用的解码方法GBK又是一致的,所以也不是乱码。

但是这时候用toHexString打印出的两个字节串是不一样的。先直接把china逐字强行转换为int的情况,不涉及输出编码,总是unicode的。(JVM规范规定class里字串必须unicode编码)只要上述(1) (2)匹配,java编译器会自动从各种编码的源文件正确转成class文件里统一unicode编码的字串。相反,作为一个题外话提一下,当(1)(2)不匹配时会在特定的一种配合(1)(2)的(3)(4)也不匹配的情况下会负负得正输出正常,但这是绝对错误的做法,因为任何要求(1)(2)和(3)(4)有匹配关系的要求都是在应用中可能无法满足的。java编译器对这种情况也会报告warning,但不fail。

综上,一旦fileencoding设成宿主 *** 作系统默认而系统consle也采用 *** 作系统默认编解码的话,(3)(4)总是一致的,无论系统选择的是GBK还是utf-8等等。

那么如果fileencoding不选系统默认呢?比如utf-8。那就很可能出现乱码了。但是,慢着,试验的结果还是没有乱码。那是因为fileencoding是静态的JVM系统参数,在程序里像楼主那样设定是不起作用的(我不知道有没有办法发一个什么通知让这种程序改变生效的)。必须作为JVM参数直接传给java程序让它构造虚拟机的时候就得到这个参数,否则JVM会去拿宿主系统的默认值,就相当于又回到设fileencoding=iso8859-1了。

java -Dfileencoding=utf-8 A

这下终于乱码了,而且两个都乱了。打印出的字节串一个还是unicode,另一个从GBK变到utf-8了。

如果你发现试验的现象和我上面说的正好相反,请注意检查console的编码设置,我们上面假设它也采用了宿主系统默认编码,但有些console很高级的嘞,可以设置成不通编码的(其实几乎所有的都可以)。那么分析的方法和上面一样,结果可能正好相反。

Java中字符串转码,根据实际运用的环境有以下三种方式

使用JavalangString

这是最常用的方法,先用对应编码获取字节,然后重新构造新编码,示例代码如下:

String s = "清山";  

byte[] b = sgetBytes("utf-8");//编码  

String sa = new String(b, "gb2312");//解码:用什么字符集编码就用什么字符集解码

   

javaioInputStreamReader/OutputStreamWriter:桥转换

读写文件的应用中,可以使用这种方式,直接在IO流构造中转换,示例代码如下:

  

InputStream is = new FileInputStream("C:/项目进度跟踪txt");//文件读取  

InputStreamReader isr = new InputStreamReader(is, "utf-8");//解码  

OutputStream os = new FileOutputStream("C:/项目进度跟踪_gb2312txt");//文件输出  

OutputStreamWriter osw = new OutputStreamWriter(os, "gb2312");//开始编码

   

javanioCharset

使用nio中的Charset转换字符,示例代码如下:

 

Charset inSet = CharsetforName("utf-8");  // 解码字符集 

Charset outSet = CharsetforName("gb2312");  // 编码字符集  

CharsetDecoder de = inSetnewDecoder();  // 解码器

CharsetEncoder en = outSetnewEncoder();// 编码

以上就是关于java里关于String的编码与解码全部的内容,包括:java里关于String的编码与解码、JAVA中字符编码的原理是什么、几种判断字符集编码的方法(Java) 未完等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9472830.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-28
下一篇 2023-04-28

发表评论

登录后才能评论

评论列表(0条)

保存