как преобразовать байтовый массив в шестнадцатеричную строку в java?

<p>у меня есть байтовый массив, заполненный шестнадцатеричными числами, и печатать его простым способом довольно бессмысленно, потому что там много непечатаемых элементов. мне нужен точный шестнадцатеричный код в виде: <code>3a5f771c</code></p>

<p>из обсуждения <a href="https://stackoverflow.com/q/332079/1284661">здесь</a> , и особенно <a href="https://stackoverflow.com/a/2197650/1284661">этого</a> ответа, это функция, которую я сейчас использую:</p><pre><code>private final static char[] hexarray = "0123456789abcdef".tochararray(); public static string bytestohex(byte[] bytes) { char[] hexchars = new char[bytes.length * 2]; for ( int j = 0; j < bytes.length; j++ ) { int v = bytes[j] & 0xff; hexchars[j * 2] = hexarray[v >>> 4]; hexchars[j * 2 + 1] = hexarray[v & 0x0f]; } return new string(hexchars); } </code></pre><p>мои собственные крошечные тесты (миллион байтов в тысячу раз, 256 байтов в 10 миллионов раз) показали, что он намного быстрее, чем любая другая альтернатива, примерно вдвое меньше для длинных массивов. по сравнению с ответом, который я получил, переключение на побитовые операции - как предложено в обсуждении - сократило время на длинные массивы примерно на 20%. (изменить: когда я говорю, что это быстрее, чем альтернативы, я имею в виду альтернативный код, предлагаемый в обсуждениях. производительность эквивалентна кодеку commons, который использует очень похожий код.)</p>

<p>библиотека <a href="http://commons.apache.org/codec/">apache commons codec</a> имеет класс <a href="http://commons.apache.org/proper/commons-codec/archives/1.10/apidocs/org/apache/commons/codec/binary/hex.html">hex</a> для выполнения этой работы.</p><pre><code>import org.apache.commons.codec.binary.hex; string foo = "i am a string"; byte[] bytes = foo.getbytes(); system.out.println( hex.encodehexstring( bytes ) ); </code></pre>

<p>используйте <code>datatypeconverter.printhexbinary()</code>. вы можете прочитать его документацию в <a href="http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/datatypeconverter.html">http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/datatypeconverter.html</a> </p><p>например:</p><pre><code>byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61}; system.out.println(javax.xml.bind.datatypeconverter.printhexbinary(bytes)); </code></pre><p>приведет к:</p><pre><code>000086003d </code></pre><p>как вы можете видеть, он будет получать шестнадцатеричную строку, представляющую массив байтов с ведущими нулями.</p><p>этот ответ в основном такой же, как в вопросе <a href="https://stackoverflow.com/questions/332079/in-java-how-do-i-convert-a-byte-array-to-a-string-of-hex-digits-while-keeping-l">в java, как я могу преобразовать байтовый массив в строку шестнадцатеричных цифр, сохраняя ведущие нули?</a> </p>

<p>самое простое решение, без внешних библиотек, без констант цифр:</p><pre><code>public static string bytearraytohex(byte[] a) { stringbuilder sb = new stringbuilder(a.length * 2); for(byte b: a) sb.append(string.format("%02x", b)); return sb.tostring(); } </code></pre>

<p>решение guava, для полноты:</p><pre><code>import com.google.common.io.baseencoding; ... byte[] bytes = "hello world".getbytes(standardcharsets.utf\_8); final string hex = baseencoding.base16().lowercase().encode(bytes); </code></pre><p>теперь <code>hex</code> является <code>"48656c6c6f20776f726c64"</code>.</p>

<p>этот простой oneliner работает для меня<br> <code>string result = new biginteger(1, inputbytes).tostring(16); </code> <br> редактировать - использование этого удалит ведущие нули, но они сработали для моего варианта использования. спасибо @voicu за указание на это</p>

<p>используйте datatypeconverter class<code>javax.xml.bind.datatypeconverter</code></p><p><code>string hexstring = datatypeconverter.printhexbinary(bytes[] raw);</code></p>

<p>при незначительной стоимости хранения таблицы соответствия эта реализация проста и очень быстра.</p><pre><code> private static final char[] byte2hex=( "000102030405060708090a0b0c0d0e0f"+ "101112131415161718191a1b1c1d1e1f"+ "202122232425262728292a2b2c2d2e2f"+ "303132333435363738393a3b3c3d3e3f"+ "404142434445464748494a4b4c4d4e4f"+ "505152535455565758595a5b5c5d5e5f"+ "606162636465666768696a6b6c6d6e6f"+ "707172737475767778797a7b7c7d7e7f"+ "808182838485868788898a8b8c8d8e8f"+ "909192939495969798999a9b9c9d9e9f"+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"+ "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff").tochararray(); ; public static string gethexstring(byte[] bytes) { final int len=bytes.length; final char[] chars=new char[len<<1]; int hexindex; int idx=0; int ofs=0; while (ofs<len) { hexindex=(bytes[ofs++] & 0xff)<<1; chars[idx++]=byte2hex[hexindex++]; chars[idx++]=byte2hex[hexindex]; } return new string(chars); } </code></pre>

<p>я нашел три разных способа здесь: <a href="http://www.rgagnon.com/javadetails/java-0596.html">http://www.rgagnon.com/javadetails/java-0596.html</a> </p><p>самым элегантным, как он также отмечает, я думаю, является этот:</p><pre><code>static final string hexes = "0123456789abcdef"; public static string gethex( byte [] raw ) { if ( raw == null ) { return null; } final stringbuilder hex = new stringbuilder( 2 * raw.length ); for ( final byte b : raw ) { hex.append(hexes.charat((b & 0xf0) >> 4)) .append(hexes.charat((b & 0x0f))); } return hex.tostring(); } </code></pre>

<p>я бы использовал что-то вроде этого для фиксированной длины, например, хэши:</p><pre><code>md5sum = string.format("%032x", new biginteger(1, md.digest())); </code></pre>

<p>как насчет этого?</p><pre><code> string bytetohex(final byte[] hash) { formatter formatter = new formatter(); for (byte b : hash) { formatter.format("%02x", b); } string result = formatter.tostring(); formatter.close(); return result; } </code></pre>

<p>я предпочитаю использовать это:</p><pre><code>final protected static char[] hexarray = "0123456789abcdef".tochararray(); public static string bytestohex(byte[] bytes, int offset, int count) { char[] hexchars = new char[count * 2]; for ( int j = 0; j < count; j++ ) { int v = bytes[j+offset] & 0xff; hexchars[j * 2] = hexarray[v >>> 4]; hexchars[j * 2 + 1] = hexarray[v & 0x0f]; } return new string(hexchars); } </code></pre><p>это немного более гибкая адаптация принятого ответа. лично я сохраняю принятый ответ и эту перегрузку вместе с ним, чтобы его можно было использовать в более широком контексте.</p>

<p>итак, есть несколько способов сделать это, но если вы решите использовать библиотеку, я бы посоветовал покопаться в вашем проекте, чтобы увидеть, было ли что-то реализовано в библиотеке, которая уже является частью вашего проекта, перед добавлением новой библиотеки. просто чтобы сделать это. например, если у вас еще нет </p><blockquote> <p>org.apache.commons.codec.binary.hex</p> </blockquote><p>может быть, у вас есть ...</p><blockquote> <p>org.apache.xerces.impl.dv.util.hexbin</p> </blockquote>

<p>я обычно использую следующий метод для заявления debuf, но я не знаю, является ли это лучшим способом сделать это или нет</p><pre><code>private static string digits = "0123456789abcdef"; public static string tohex(byte[] data){ stringbuffer buf = new stringbuffer(); for (int i = 0; i != data.length; i++) { int v = data[i] & 0xff; buf.append(digits.charat(v >> 4)); buf.append(digits.charat(v & 0xf)); } return buf.tostring(); } </code></pre>

<p>на этой странице не найдено ни одного решения, которое бы не</p><ol> <li>используйте цикл</li> <li>используйте javax.xml.bind.datatypeconverter, который прекрасно компилируется, но часто выдает java.lang.noclassdeffounderror во время выполнения.</li> </ol><p>вот решение, в котором нет недостатков, указанных выше (хотя никаких обещаний у меня нет и других недостатков)</p><pre><code>import java.math.biginteger; import static java.lang.system.out; public final class app2 { // | proposed solution. public static string encode(byte[] bytes) { final int length = bytes.length; // | biginteger constructor throws if it is given an empty array. if (length == 0) { return "00"; } final int evenlength = (int)(2 * math.ceil(length / 2.0)); final string format = "%0" + evenlength + "x"; final string result = string.format (format, new biginteger(bytes)); return result; } public static void main(string[] args) throws exception { // 00 out.println(encode(new byte[] {})); // 01 out.println(encode(new byte[] {1})); //203040 out.println(encode(new byte[] {0x20, 0x30, 0x40})); // 416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e out.println(encode("all your base are belong to us.".getbytes())); } } </code></pre><p>я не мог получить это под 62 кодами операций, но если вы можете жить без заполнения 0 в случае, если первый байт меньше 0x10, то в следующем решении используются только 23 кода операции. действительно показывает, как «легко реализовать себя» такие решения, как «pad с нулем, если длина строки нечетная», могут стать довольно дорогими, если собственная реализация еще не доступна (или в этом случае, если у biginteger была опция префикса с нулями в нанизывать).</p><pre><code>public static string encode(byte[] bytes) { final int length = bytes.length; // | biginteger constructor throws if it is given an empty array. if (length == 0) { return "00"; } return new biginteger(bytes).tostring(16); } </code></pre>

<p>небольшой вариант решения, предложенного @maybewecouldstealavan, который позволяет визуально объединять n байтов в выходную шестнадцатеричную строку:</p><pre class="lang-java prettyprint-override"><code> final static char[] hex\_array = "0123456789abcdef".tochararray(); final static char bundle\_sep = ' '; public static string bytestohexstring(byte[] bytes, int bundlesize /*[bytes]*/]) { char[] hexchars = new char[(bytes.length * 2) + (bytes.length / bundlesize)]; for (int j = 0, k = 1; j < bytes.length; j++, k++) { int v = bytes[j] & 0xff; int start = (j * 2) + j/bundlesize; hexchars[start] = hex\_array[v >>> 4]; hexchars[start + 1] = hex\_array[v & 0x0f]; if ((k % bundlesize) == 0) { hexchars[start + 2] = bundle\_sep; } } return new string(hexchars).trim(); } </code></pre><p>то есть:</p><pre class="lang-java prettyprint-override"><code>bytestohexstring("..doom..".tochararray().getbytes(), 2); 2e2e 444f 4f4d 2e2e bytestohexstring("..doom..".tochararray().getbytes(), 4); 2e2e444f 4f4d2e2e </code></pre>

<p>если вы используете платформу spring security, вы можете использовать:</p><pre><code>import org.springframework.security.crypto.codec.hex final string teststring = "test string"; final byte[] bytearray = teststring.getbytes(); system.out.println(hex.encode(bytearray)); </code></pre>

<p>мое решение основано на решении может быть wecouldstealavan, но не опирается на какие-либо дополнительные таблицы поиска. он не использует взломы типа int-to-char (на самом деле, <code>character.fordigit()</code> делает это, выполняя некоторое сравнение, чтобы проверить, что на самом деле является цифрой) и, следовательно, может быть немного медленнее. пожалуйста, не стесняйтесь использовать его где угодно. приветствия.</p><pre><code>public static string bytestohex(final byte[] bytes) { final int numbytes = bytes.length; final char[] container = new char[numbytes * 2]; for (int i = 0; i < numbytes; i++) { final int b = bytes[i] & 0xff; container[i * 2] = character.fordigit(b >>> 4, 0x10); container[i * 2 + 1] = character.fordigit(b & 0xf, 0x10); } return new string(container); } </code></pre>

<pre><code> converts bytes data to hex characters @param bytes byte array to be converted to hex string @return byte string in hex format private static string bytestohex(byte[] bytes) { char[] hexchars = new char[bytes.length * 2]; int v; for (int j = 0; j < bytes.length; j++) { v = bytes[j] & 0xff; hexchars[j * 2] = hex\_array[v >>> 4]; hexchars[j * 2 + 1] = hex\_array[v & 0x0f]; } return new string(hexchars); } </code></pre>

<p>вот реализация <code>java.util.base64</code>- (частичная), не правда ли?</p><pre><code>public class base16/*a.k.a. hex*/ { public static class encoder{ private static char[] tolowerhex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; private static char[] toupperhex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; private boolean upper; public encoder(boolean upper) { this.upper=upper; } public string encode(byte[] data){ char[] value=new char[data.length*2]; char[] tohex=upper?toupperhex:tolowerhex; for(int i=0,j=0;i<data.length;i++){ int octet=data[i]&0xff; value[j++]=tohex[octet>>4]; value[j++]=tohex[octet&0xf]; } return new string(value); } static final encoder lower=new encoder(false); static final encoder upper=new encoder(true); } public static encoder getencoder(){ return encoder.lower; } public static encoder getupperencoder(){ return encoder.upper; } //... } </code></pre>

<pre><code> public static byte[] hexstringtobytearray(string s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((character.digit(s.charat(i), 16) << 4) + character.digit(s.charat(i+1), 16)); } return data; } </code></pre>

<p>// сдвиг байтов более эффективен // вы также можете использовать этот </p><pre><code>public static string gethexstring (string s) { byte[] buf = s.getbytes(); stringbuffer sb = new stringbuffer(); for (byte b:buf) { sb.append(string.format("%x", b)); } return sb.tostring(); } </code></pre>

<p>если вы ищете такой же байтовый массив для python, я преобразовал эту реализацию java в python.</p><pre><code>class bytearray: @classmethod def char(cls, args=[]): cls.hexarray = "0123456789abcdef".encode('utf-16') j = 0 length = (cls.hexarray) if j < length: v = j & 0xff hexchars = [none, none] hexchars[j * 2] = str( cls.hexarray) + str(v) hexchars[j * 2 + 1] = str(cls.hexarray) + str(v) + str(0x0f) # use if you want... #hexchars.pop() return str(hexchars) array = bytearray() print array.char(args=[]) </code></pre>

<pre><code>private static string bytestohexstring(byte[] bytes, int length) { if (bytes == null || length == 0) return null; stringbuilder ret = new stringbuilder(2*length); for (int i = 0 ; i < length ; i++) { int b; b = 0x0f & (bytes[i] >> 4); ret.append("0123456789abcdef".charat(b)); b = 0x0f & bytes[i]; ret.append("0123456789abcdef".charat(b)); } return ret.tostring(); } </code></pre>