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) 未完等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)