Java中String和byte[]间的 转换浅析
Java语言中字符串类型和字节数组类型相互之间的转换经常发生,网上的分析及代码也比较多,本文将分析总结常规的byte[]和String间的转换以及十六进制String和byte[]间相互转换的原理及实现。
1. String转byte[]
首先我们来分析一下常规的String转byte[]的方法,代码如下:
public static byte[] strToByteArray(String str) {
if (str == null) {
return null;
}
byte[] byteArray = str.getBytes();
return byteArray;
}
很简单,就是调用String类的getBytes()方法。看JDK源码可以发现该方法最终调用了String类如下的方法。
/**
* JDK source code
*/
public byte[] getBytes(Charset charset) {
String canonicalCharsetName = charset.name();
if (canonicalCharsetName.equals("UTF-8")) {
return Charsets.toUtf8Bytes(value, offset, count);
} else if (canonicalCharsetName.equals("ISO-8859-1")) {
return Charsets.toIsoLatin1Bytes(value, offset, count);
} else if (canonicalCharsetName.equals("US-ASCII")) {
return Charsets.toAsciiBytes(value, offset, count);
} else if (canonicalCharsetName.equals("UTF-16BE")) {
return Charsets.toBigEndianUtf16Bytes(value, offset, count);
} else {
CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count);
ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer());
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes);
return bytes;
}
}
上述代码其实就是根据给定的编码方式进行编码。如果调用的是不带参数的getBytes()方法,则使用默认的编码方式,如下代码所示:
/**
* JDK source code
*/
private static Charset getDefaultCharset() {
String encoding = System.getProperty("file.encoding", "UTF-8");
try {
return Charset.forName(encoding);
} catch (UnsupportedCharsetException e) {
return Charset.forName("UTF-8");
}
}
关于默认的编码方式,Java API是这样说的:
The default charset is determined during virtual-machine startup and typically depends upon the locale and charset of the underlying operating system.
同样,由上述代码可以看出,默认编码方式是由System类的"file.encoding"属性决定的,经过测试,在简体中文Windows操作系统下,默认编码方式为"GBK",在Android平台上,默认编码方式为"UTF-8"。
2. byte[]转String
接下来分析一下常规的byte[]转为String的方法,代码如下:
public static String byteArrayToStr(byte[] byteArray) {
if (byteArray == null) {
return null;
}
String str = new String(byteArray);
return str;
}
很简单,就是String的构造方法之一。那我们分析Java中String的源码,可以看出所有以byte[]为参数的构造方法最终都调用了如下代码所示的构造方法。需要注意的是Java中String类的数据是Unicode类型的,因此上述的getBytes()方法是把Unicode类型转化为指定编码方式的byte数组;而这里的Charset为读取该byte数组时所使用的编码方式。
/**
* JDK source code
*/
public String(byte[] data, int offset, int byteCount, Charset charset) {
if ((offset | byteCount) < 0 || byteCount > data.length - offset) {
throw failedBoundsCheck(data.length, offset, byteCount);
}
// We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because
// 'count' and 'value' are final.
String canonicalCharsetName = charset.name();
if (canonicalCharsetName.equals("UTF-8")) {
byte[] d = data;
char[] v = new char[byteCount];
int idx = offset;
int last = offset + byteCount;
int s = 0;
outer:
while (idx < last) {
byte b0 = d[idx++];
if ((b0 & 0x80) == 0) {
// 0xxxxxxx
// Range: U-00000000 - U-0000007F
int val = b0 & 0xff;
v[s++] = (char) val;
} else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) ||
((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe)
== 0xfc)) {
int utfCount = 1;
if ((b0 & 0xf0) == 0xe0) utfCount = 2;
else if ((b0 & 0xf8) == 0xf0) utfCount = 3;
else if ((b0 & 0xfc) == 0xf8) utfCount = 4;
else if ((b0 & 0xfe) == 0xfc) utfCount = 5;
// 110xxxxx (10xxxxxx)+
// Range: U-00000080 - U-000007FF (count == 1)
// Range: U-00000800 - U-0000FFFF (count == 2)
// Range: U-00010000 - U-001FFFFF (count == 3)
// Range: U-00200000 - U-03FFFFFF (count == 4)
// Range: U-04000000 - U-7FFFFFFF (count == 5)
if (idx + utfCount > last) {
v[s++] = REPLACEMENT_CHAR;
continue;
}
// Extract usable bits from b0
int val = b0 & (0x1f >> (utfCount - 1));
for (int i = 0; i < utfCount; ++i) {
byte b = d[idx++];
if ((b & 0xc0) != 0x80) {
v[s++] = REPLACEMENT_CHAR;
idx--; // Put the input char back
continue outer;
}
// Push new bits in from the right side
val <<= 6;
val |= b & 0x3f;
}
// Note: Java allows overlong char
// specifications To disallow, check that val
// is greater than or equal to the minimum
// value for each count:
//
// count min value
// ----- ----------
// 1 0x80
// 2 0x800
// 3 0x10000
// 4 0x200000
// 5 0x4000000
// Allow surrogate values (0xD800 - 0xDFFF) to
// be specified using 3-byte UTF values only
if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) {
v[s++] = REPLACEMENT_CHAR;
continue;
}
// Reject chars greater than the Unicode maximum of U+10FFFF.
if (val > 0x10FFFF) {
v[s++] = REPLACEMENT_CHAR;
continue;
}
// Encode chars from U+10000 up as surrogate pairs
if (val < 0x10000) {
v[s++] = (char) val;
} else {
int x = val & 0xffff;
int u = (val >> 16) & 0x1f;
int w = (u - 1) & 0xffff;
int hi = 0xd800 | (w << 6) | (x >> 10);
int lo = 0xdc00 | (x & 0x3ff);
v[s++] = (char) hi;
v[s++] = (char) lo;
}
} else {
// Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff
v[s++] = REPLACEMENT_CHAR;
}
}
if (s == byteCount) {
// We guessed right, so we can use our temporary array as-is.
this.offset = 0;
this.value = v;
this.count = s;
} else {
// Our temporary array was too big, so reallocate and copy.
this.offset = 0;
this.value = new char[s];
this.count = s;
System.arraycopy(v, 0, value, 0, s);
}
} else if (canonicalCharsetName.equals("ISO-8859-1")) {
this.offset = 0;
this.value = new char[byteCount];
this.count = byteCount;
Charsets.isoLatin1BytesToChars(data, offset, byteCount, value);
} else if (canonicalCharsetName.equals("US-ASCII")) {
this.offset = 0;
this.value = new char[byteCount];
this.count = byteCount;
Charsets.asciiBytesToChars(data, offset, byteCount, value);
} else {
CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount));
this.offset = 0;
this.count = cb.length();
if (count > 0) {
// We could use cb.array() directly, but that would mean we'd have to trust
// the CharsetDecoder doesn't hang on to the CharBuffer and mutate it later,
// which would break String's immutability guarantee. It would also tend to
// mean that we'd be wasting memory because CharsetDecoder doesn't trim the
// array. So we copy.
this.value = new char[count];
System.arraycopy(cb.array(), 0, value, 0, count);
} else {
this.value = EmptyArray.CHAR;
}
}
}
具体的转换过程较为复杂,其实就是将byte数组的一个或多个元素按指定的Charset类型读取并转换为char类型(char本身就是以Unicode编码方式存储的),因为String类的核心是其内部维护的char数组。因此有兴趣的同学可以研究下各种编码方式的编码规则,然后才能看懂具体的转换过程。
3. byte[]转十六进制String
所谓十六进制String,就是字符串里面的字符都是十六进制形式,因为一个byte是八位,可以用两个十六进制位来表示,因此,byte数组中的每个元素可以转换为两个十六进制形式的char,所以最终的HexString的长度是byte数组长度的两倍。闲话少说上代码:
public static String byteArrayToHexStr(byte[] byteArray) {
if (byteArray == null){
return null;
}
char[] hexArray = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[byteArray.length * 2];
for (int j = 0; j < byteArray.length; j++) {
int v = byteArray[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
上述代码中,之所以要将byte数值和0xFF按位与,是因为我们为了方便后面的无符号移位操作(无符号右移运算符>>>只对32位和64位的值有意义),要将byte数据转换为int类型,而如果直接转换就会出现问题。因为java里面二进制是以补码形式存在的,如果直接转换,位扩展会产生问题,如值为-1的byte存储的二进制形式为其补码11111111,而转换为int后为11111111111111111111111111111111,直接使用该值结果就不对了。而0xFF默认是int类型,即0x000000FF,一个byte值跟0xFF相与会先将那个byte值转化成int类型运算,这样,相与的结果中高的24个比特就总会被清0,后面的运算才会正确。
4. 十六进制String转byte[]
没什么好说的了,就是byte[]转十六进制String的逆过程,放代码:
public static byte[] hexStrToByteArray(String str)
{
if (str == null) {
return null;
}
if (str.length() == 0) {
return new byte[0];
}
byte[] byteArray = new byte[str.length() / 2];
for (int i = 0; i < byteArray.length; i++){
String subStr = str.substring(2 * i, 2 * i + 2);
byteArray[i] = ((byte)Integer.parseInt(subStr, 16));
}
return byteArray;
}
文中所有代码可以在个人github主页查看和下载。
另,个人技术博客,同步更新,欢迎关注!转载请注明出处!文中若有什么错误希望大家探讨指正!
Java中String和byte[]间的 转换浅析的更多相关文章
- Java中String和byte[]间的转换浅析
Java语言中字符串类型和字节数组类型相互之间的转换经常发生,网上的分析及代码也比较多,本文将分析总结常规的byte[]和String间的转换以及十六进制String和byte[]间相互转换的原理及实 ...
- Java中String和byte[]间的 转换
数据库的字段中使用了blob类型时,在entity中此字段可以对应为byte[] 类型,保存到数据库中时需要把传入的参数转为byte[]类型,读取的时候再通过将byte[]类型转换为String类型. ...
- java中string与byte[]的转换
1.string 转 byte[] byte[] midbytes=isoString.getBytes("UTF8"); //为UTF8编码 byte[] isoret = sr ...
- 转:Java中String与byte[]的转换
原文地址:http://blog.csdn.net/llwan/article/details/7567906 String s = "fs123fdsa";//String变量 ...
- java中string与byte[]之间的转化分析
背景:最近接触zookeeper的java开发,由于zookeeper中传的好像都是byte[]的数据(需要进一步确认),好多情况下都需要进行转换. 1)和zookeeper原生API不同,通过zkc ...
- Java 中 String 的构造方法
String 对于所有 Java 程序员来说都不会陌生,几乎每天甚至每个程序都会和 String 打交道,因此将 String 的常用知识汇集在此,方便查阅. 概叙: Java 中是如此定义 Stri ...
- 关于==和equals()方法&Java中string与char如何转换&String,StringBuffer
1.对于基本数据类型,可以直接使用==和!=进行内容比较 如:int x=30; int y=30; x==y; //true 基本数据类型 简单类型(基本类型) bo ...
- Java中String转换Double类型 Java小数点后留两位
Java中String转换Double类型 double num1 = 0.0; String qq = "19.987"; num1 = Double.valueOf(qq.to ...
- java中String类学习
java中String类的相关操作如下: (1)初始化:例如,String s = “abc”; (2)length:返回字符串的长度. (3)charAT:字符操作,按照索引值获得字符串中的指定字符 ...
随机推荐
- 20145335郝昊《网络对抗》Exp 8 Web基础
20145335郝昊<网络对抗>Exp 8 Web基础 实验内容 本实践的具体要求有: Web前端HTML:能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法, ...
- Asp.net简单概念知识
1. 简述 private. protected. public. internal 修饰符的访问权限.答 . private : 私有成员, 在类的内部才可以访问. protected ...
- POJ2528 Mayor's posters(线段树&区间更新+离散化)题解
题意:给一个区间,表示这个区间贴了一张海报,后贴的会覆盖前面的,问最后能看到几张海报. 思路: 之前就不会离散化,先讲一下离散化:这里离散化的原理是:先把每个端点值都放到一个数组中并除重+排序,我们就 ...
- 委托的begininvoke
http://blog.csdn.net/cml2030/article/details/2172854 http://blog.163.com/weizhiyong_111/blog/static/ ...
- MVC ---- ckeditor 批量绑定 blur 事件
在项目遇到个问题,就是把循环出来的ckeditor 批量添加 blur 事件,折腾了2天 终于搞定 @{ ].Rows) { <table class="ui-jqgrid-btabl ...
- circRNA研究手册
环状RNA(circRNA)研究技术手册.doc.pdf (转自:汉恒生物)
- python 集合取最大最小值
#Create a set seta = , , , , , ]) #Find maximum value print(max(seta)) #Find minimum value print(min ...
- MongoDB(课时11 嵌套集合)
3.4.2.6 嵌套集合运算 MongoDB数据库里每个集合数据可以继续保存其它的集合数据.例如:有些学生信息中需要保存家长信息. 范例: 增加数据 db.students.insert({" ...
- Windows下使用pip安装python包是报错-UnicodeDecodeError: 'ascii' codec can't decode byte 0xcb in position 0
先交待下开发环境: 操作系统:Windows 7 Python版本:2.7.9 Pip版本:6.1.1 其他环境忽略 在windows下使用pip下载python包,出现如下错误 Collecting ...
- 使用 data.table 包操作数据
在第一节中,我们回顾了许多用于操作数据框的内置函数.然后,了解了 sqldf 扩展包,它使得简单的数据查询和统计变得更简便.然而,两种方法都有各自的局限性.使用内置函数可能既繁琐又缓慢,而相对于各式各 ...