您现在的位置: 365建站网 > 365文章 > java String和byte[]转换(包括16进制String和byte[]转换)

java String和byte[]转换(包括16进制String和byte[]转换)

文章来源:365jz.com     点击数:289    更新时间:2017-08-28 10:51   参与评论

Java语言中字符串类型和字节数组类型相互之间的转换经常发生,网上的分析及代码也比较多,本文将分析总结常规的byte[]和String间的转换以及十六进制String和byte[]间相互转换的原理及实现。

1. String转byte[]

首先我们来分析一下常规的String转byte[]的方法,代码如下:

</>code

  1. public static byte[] strToByteArray(String str) {
  2. if (str == null) {
  3. return null;
  4. }
  5. byte[] byteArray = str.getBytes();
  6. return byteArray;
  7. }

很简单,就是调用String类的getBytes()方法。看JDK源码可以发现该方法最终调用了String类如下的方法。

</>code

  1. /**
  2. * JDK source code
  3. */
  4. public byte[] getBytes(Charset charset) {
  5. String canonicalCharsetName = charset.name();
  6. if (canonicalCharsetName.equals("UTF-8")) {
  7. return Charsets.toUtf8Bytes(value, offset, count);
  8. } else if (canonicalCharsetName.equals("ISO-8859-1")) {
  9. return Charsets.toIsoLatin1Bytes(value, offset, count);
  10. } else if (canonicalCharsetName.equals("US-ASCII")) {
  11. return Charsets.toAsciiBytes(value, offset, count);
  12. } else if (canonicalCharsetName.equals("UTF-16BE")) {
  13. return Charsets.toBigEndianUtf16Bytes(value, offset, count);
  14. } else {
  15. CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count);
  16. ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer());
  17. byte[] bytes = new byte[buffer.limit()];
  18. buffer.get(bytes);
  19. return bytes;
  20. }
  21. }

上述代码其实就是根据给定的编码方式进行编码。如果调用的是不带参数的getBytes()方法,则使用默认的编码方式,如下代码所示:

</>code

  1. /**
  2. * JDK source code
  3. */
  4. private static Charset getDefaultCharset() {
  5. String encoding = System.getProperty("file.encoding", "UTF-8");
  6. try {
  7. return Charset.forName(encoding);
  8. } catch (UnsupportedCharsetException e) {
  9. return Charset.forName("UTF-8");
  10. }
  11. }

关于默认的编码方式,JavAPI是这样说的:

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的方法,代码如下:

</>code

  1. public static String byteArrayToStr(byte[] byteArray) {
  2. if (byteArray == null) {
  3. return null;
  4. }
  5. String str = new String(byteArray);
  6. return str;
  7. }

很简单,就是String的构造方法之一。那我们分析Java中String的源码,可以看出所有以byte[]为参数的构造方法最终都调用了如下代码所示的构造方法。需要注意的是Java中String类的数据是Unicode类型的,因此上述的getBytes()方法是把Unicode类型转化为指定编码方式的byte数组;而这里的Charset为读取该byte数组时所使用的编码方式。

</>code

  1. /**
  2. * JDK source code
  3. */
  4. public String(byte[] data, int offset, int byteCount, Charset charset) {
  5. if ((offset | byteCount) < 0 || byteCount > data.length - offset) {
  6. throw failedBoundsCheck(data.length, offset, byteCount);
  7. }
  8. // We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because
  9. // 'count' and 'value' are final.
  10. String canonicalCharsetName = charset.name();
  11. if (canonicalCharsetName.equals("UTF-8")) {
  12. byte[] d = data;
  13. char[] v = new char[byteCount];
  14. int idx = offset;
  15. int last = offset + byteCount;
  16. int s = 0;
  17. outer:
  18. while (idx < last) {
  19. byte b0 = d[idx++];
  20. if ((b0 & 0x80) == 0) {
  21. // 0xxxxxxx
  22. // Range: U-00000000 - U-0000007F
  23. int val = b0 & 0xff;
  24. v[s++] = (char) val;
  25. } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) ||
  26. ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe)
  27. == 0xfc)) {
  28. int utfCount = 1;
  29. if ((b0 & 0xf0) == 0xe0) utfCount = 2;
  30. else if ((b0 & 0xf8) == 0xf0) utfCount = 3;
  31. else if ((b0 & 0xfc) == 0xf8) utfCount = 4;
  32. else if ((b0 & 0xfe) == 0xfc) utfCount = 5;
  33. // 110xxxxx (10xxxxxx)+
  34. // Range: U-00000080 - U-000007FF (count == 1)
  35. // Range: U-00000800 - U-0000FFFF (count == 2)
  36. // Range: U-00010000 - U-001FFFFF (count == 3)
  37. // Range: U-00200000 - U-03FFFFFF (count == 4)
  38. // Range: U-04000000 - U-7FFFFFFF (count == 5)
  39. if (idx + utfCount > last) {
  40. v[s++] = REPLACEMENT_CHAR;
  41. continue;
  42. }
  43. // Extract usable bits from b0
  44. int val = b0 & (0x1f >> (utfCount - 1));
  45. for (int i = 0; i < utfCount; ++i) {
  46. byte b = d[idx++];
  47. if ((b & 0xc0) != 0x80) {
  48. v[s++] = REPLACEMENT_CHAR;
  49. idx--; // Put the input char back
  50. continue outer;
  51. }
  52. // Push new bits in from the right side
  53. val <<= 6;
  54. val |= b & 0x3f;
  55. }
  56. // Note: Java allows overlong char
  57. // specifications To disallow, check that val
  58. // is greater than or equal to the minimum
  59. // value for each count:
  60. //
  61. // count min value
  62. // ----- ----------
  63. // 1 0x80
  64. // 2 0x800
  65. // 3 0x10000
  66. // 4 0x200000
  67. // 5 0x4000000
  68. // Allow surrogate values (0xD800 - 0xDFFF) to
  69. // be specified using 3-byte UTF values only
  70. if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) {
  71. v[s++] = REPLACEMENT_CHAR;
  72. continue;
  73. }
  74. // Reject chars greater than the Unicode maximum of U+10FFFF.
  75. if (val > 0x10FFFF) {
  76. v[s++] = REPLACEMENT_CHAR;
  77. continue;
  78. }
  79. // Encode chars from U+10000 up as surrogate pairs
  80. if (val < 0x10000) {
  81. v[s++] = (char) val;
  82. } else {
  83. int x = val & 0xffff;
  84. int u = (val >> 16) & 0x1f;
  85. int w = (u - 1) & 0xffff;
  86. int hi = 0xd800 | (w << 6) | (x >> 10);
  87. int lo = 0xdc00 | (x & 0x3ff);
  88. v[s++] = (char) hi;
  89. v[s++] = (char) lo;
  90. }
  91. } else {
  92. // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff
  93. v[s++] = REPLACEMENT_CHAR;
  94. }
  95. }
  96. if (s == byteCount) {
  97. // We guessed right, so we can use our temporary array as-is.
  98. this.offset = 0;
  99. this.value = v;
  100. this.count = s;
  101. } else {
  102. // Our temporary array was too big, so reallocate and copy.
  103. this.offset = 0;
  104. this.value = new char[s];
  105. this.count = s;
  106. System.arraycopy(v, 0, value, 0, s);
  107. }
  108. } else if (canonicalCharsetName.equals("ISO-8859-1")) {
  109. this.offset = 0;
  110. this.value = new char[byteCount];
  111. this.count = byteCount;
  112. Charsets.isoLatin1BytesToChars(data, offset, byteCount, value);
  113. } else if (canonicalCharsetName.equals("US-ASCII")) {
  114. this.offset = 0;
  115. this.value = new char[byteCount];
  116. this.count = byteCount;
  117. Charsets.asciiBytesToChars(data, offset, byteCount, value);
  118. } else {
  119. CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount));
  120. this.offset = 0;
  121. this.count = cb.length();
  122. if (count > 0) {
  123. // We could use cb.array() directly, but that would mean we'd have to trust
  124. // the CharsetDecoder doesn't hang on to the CharBuffer and mutate it later,
  125. // which would break String's immutability guarantee. It would also tend to
  126. // mean that we'd be wasting memory because CharsetDecoder doesn't trim the
  127. // array. So we copy.
  128. this.value = new char[count];
  129. System.arraycopy(cb.array(), 0, value, 0, count);
  130. } else {
  131. this.value = EmptyArray.CHAR;
  132. }
  133. }
  134. }

具体的转换过程较为复杂,其实就是将byte数组的一个或多个元素按指定的Charset类型读取并转换为char类型(char本身就是以Unicode编码方式存储的),因为String类的核心是其内部维护的char数组。因此有兴趣的同学可以研究下各种编码方式的编码规则,然后才能看懂具体的转换过程。

3. byte[]转十六进制String

所谓十六进制String,就是字符串里面的字符都是十六进制形式,因为一个byte是八位,可以用两个十六进制位来表示,因此,byte数组中的每个元素可以转换为两个十六进制形式的char,所以最终的HexString的长度是byte数组长度的两倍。闲话少说上代码:

</>code

  1. public static String byteArrayToHexStr(byte[] byteArray) {
  2. if (byteArray == null){
  3. return null;
  4. }
  5. char[] hexArray = "0123456789ABCDEF".toCharArray();
  6. char[] hexChars = new char[byteArray.length * 2];
  7. for (int j = 0; j < byteArray.length; j++) {
  8. int v = byteArray[j] & 0xFF;
  9. hexChars[j * 2] = hexArray[v >>> 4];
  10. hexChars[j * 2 + 1] = hexArray[v & 0x0F];
  11. }
  12. return new String(hexChars);
  13. }

上述代码中,之所以要将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的逆过程,放代码:

</>code

  1. public static byte[] hexStrToByteArray(String str)
  2. {
  3. if (str == null) {
  4. return null;
  5. }
  6. if (str.length() == 0) {
  7. return new byte[0];
  8. }
  9. byte[] byteArray = new byte[str.length() / 2];
  10. for (int i = 0; i < byteArray.length; i++){
  11. String subStr = str.substring(2 * i, 2 * i + 2);
  12. byteArray[i] = ((byte)Integer.parseInt(subStr, 16));
  13. }
  14. return byteArray;
  15. }

如对本文有疑问,请提交到交流论坛,广大热心网友会为你解答!! 点击进入论坛

发表评论 (289人查看0条评论)
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
昵称:
最新评论
------分隔线----------------------------

快速入口

· 365软件
· 杰创官网
· 建站工具
· 网站大全

其它栏目

· 建站教程
· 365学习

业务咨询

· 技术支持
· 服务时间:9:00-18:00
365建站网二维码

Powered by 365建站网 RSS地图 HTML地图

copyright © 2013-2024 版权所有 鄂ICP备17013400号