java - जावा का उपयोग करके एक बाइट सरणी में हेक्स डंप की स्ट्रिंग प्रस्तुति को कनवर्ट करें?




byte hex (16)

मैं एक लंबी स्ट्रिंग (डंप से) को परिवर्तित करने का एक तरीका ढूंढ रहा हूं, जो हेक्स मानों को बाइट सरणी में दर्शाता है।

मैं उस व्यक्ति से बेहतर वाक्यांश नहीं लगा सकता जिसने एक ही प्रश्न पोस्ट किया था:

http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_21062554.html

लेकिन इसे मूल रखने के लिए, मैं इसे अपने तरीके से वाक्यांश दूंगा: मान लीजिए कि मेरे पास एक स्ट्रिंग "00 ए0 बीएफ" है जिसे मैं बाइट [] {0x00,0xA0,0xBf} के रूप में व्याख्या करना चाहता हूं, मुझे क्या करना चाहिए?

मैं जावा नौसिखिया हूं और बिगइंटर का उपयोग कर समाप्त हुआ और हेक्स शून्य के लिए देख रहा हूं। लेकिन मुझे लगता है कि यह बदसूरत है और मुझे यकीन है कि मुझे कुछ आसान याद आ रहा है ...


एक-लाइनर्स:

import javax.xml.bind.DatatypeConverter;

public static String toHexString(byte[] array) {
    return DatatypeConverter.printHexBinary(array);
}

public static byte[] toByteArray(String s) {
    return DatatypeConverter.parseHexBinary(s);
}

FractalizeR से एक-लाइनर के पीछे वास्तविक कोड में दिलचस्पी रखने वालों के लिए (मुझे इसकी आवश्यकता है क्योंकि javax.xml.bind एंड्रॉइड (डिफ़ॉल्ट रूप से) के लिए उपलब्ध नहीं है), यह com.sun.xml.internal.bind.DatatypeConverterImpl.java से आता है com.sun.xml.internal.bind.DatatypeConverterImpl.java :

public byte[] parseHexBinary(String s) {
    final int len = s.length();

    // "111" is not a valid hex encoding.
    if( len%2 != 0 )
        throw new IllegalArgumentException("hexBinary needs to be even-length: "+s);

    byte[] out = new byte[len/2];

    for( int i=0; i<len; i+=2 ) {
        int h = hexToBin(s.charAt(i  ));
        int l = hexToBin(s.charAt(i+1));
        if( h==-1 || l==-1 )
            throw new IllegalArgumentException("contains illegal character for hexBinary: "+s);

        out[i/2] = (byte)(h*16+l);
    }

    return out;
}

private static int hexToBin( char ch ) {
    if( '0'<=ch && ch<='9' )    return ch-'0';
    if( 'A'<=ch && ch<='F' )    return ch-'A'+10;
    if( 'a'<=ch && ch<='f' )    return ch-'a'+10;
    return -1;
}

private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length*2);
    for ( byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}

Java.math से BigInteger() विधि बहुत धीमी है और अनुशंसित नहीं है।

Integer.parseInt(HEXString, 16)

डिजिट / इंटीजर में परिवर्तित किए बिना कुछ पात्रों के साथ समस्याएं पैदा कर सकती हैं

एक अच्छी तरह से कार्य विधि:

Integer.decode("0xXX") .byteValue()

समारोह:

public static byte[] HexStringToByteArray(String s) {
    byte data[] = new byte[s.length()/2];
    for(int i=0;i < s.length();i+=2) {
        data[i/2] = (Integer.decode("0x"+s.charAt(i)+s.charAt(i+1))).byteValue();
    }
    return data;
}

मज़ा करो, शुभकामनाएँ


अब तक का सबसे साफ समाधान नहीं है। लेकिन यह मेरे लिए काम करता है और अच्छी तरह से स्वरूपित है:

private String createHexDump(byte[] msg, String description) {
    System.out.println();
    String result = "\n" + description;
    int currentIndex = 0;
    for(int i=0 ; i<msg.length ; i++){
        currentIndex++;
        if(i == 0){
            result += String.format("\n  %04x ", i);
        }
        if(i % 16 == 0 && i != 0){
            result += " | ";
            for(int j=(i-16) ; j<msg.length && j<i ; j++) {
                char characterToAdd = (char) msg[j];
                if (characterToAdd == '\n') {
                    characterToAdd = ' ';
                }
                result += characterToAdd;
            }

            result += String.format("\n  %04x ", i);
        }

        result += String.format("%02x ", msg[i]);
    }

    if(currentIndex % 16 != 0){
        int fitIns = msg.length / 16;
        int leftOvers = msg.length - (fitIns * 16);
        for(int i=0 ; i<16-leftOvers ; i++){
            result += "   ";
        }

        result += " | ";

        for(int i=msg.length-leftOvers ; i<msg.length ; i++){
            char characterToAdd = (char) msg[i];
            if (characterToAdd == '\n') {
                characterToAdd = ' ';
            }
            result += characterToAdd;
        }
    }

    result += "\n";

    return result;
}

उत्पादन:

  S -> C
    0000 0b 00 2e 06 4d 6f 72 69 74 7a 53 6f 6d 65 20 54  |  .Heyyy Some T
    0010 43 50 20 73 74 75 66 66 20 49 20 63 61 70 74 75  | CP stuff I captu
    0020 72 65 64 2e 2e 77 65 6c 6c 20 66 6f 72 6d 61 74  | red..well format
    0030 3f                                               | ?

असल में, मुझे लगता है कि BigInteger समाधान बहुत अच्छा है:

new BigInteger("00A0BF", 16).toByteArray();

संपादित करें: पोस्टर द्वारा नोट किए गए प्रमुख शून्यों के लिए सुरक्षित नहीं है


एंड्रॉइड में, यदि आप हेक्स के साथ काम कर रहे हैं, तो आप okio आज़मा सकते हैं।

सरल उपयोग:

byte[] bytes = ByteString.decodeHex("c000060000").toByteArray();

और परिणाम होगा

[-64, 0, 6, 0, 0]

ओप वोट समाधान के आधार पर, निम्नलिखित थोड़ा अधिक कुशल होना चाहिए:

  public static byte [] hexStringToByteArray (final String s) {
    if (s == null || (s.length () % 2) == 1)
      throw new IllegalArgumentException ();
    final char [] chars = s.toCharArray ();
    final int len = chars.length;
    final byte [] data = new byte [len / 2];
    for (int i = 0; i < len; i += 2) {
      data[i / 2] = (byte) ((Character.digit (chars[i], 16) << 4) + Character.digit (chars[i + 1], 16));
    }
    return data;
  }

क्योंकि: एक चार सरणी में प्रारंभिक रूपांतरण charAt में लंबाई जांच को बढ़ाता है


बर्ट रीजीलिंक द्वारा प्रस्तुत कोड बस काम नहीं करता है। निम्नलिखित आज़माएं:

import javax.xml.bind.DatatypeConverter;
import java.io.*;

public class Test
{  
    @Test
    public void testObjectStreams( ) throws IOException, ClassNotFoundException
    {     
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);

            String stringTest = "TEST";
            oos.writeObject( stringTest );

            oos.close();
            baos.close();

            byte[] bytes = baos.toByteArray();
            String hexString = DatatypeConverter.printHexBinary( bytes);
            byte[] reconvertedBytes = DatatypeConverter.parseHexBinary(hexString);

            assertArrayEquals( bytes, reconvertedBytes );

            ByteArrayInputStream bais = new ByteArrayInputStream(reconvertedBytes);
            ObjectInputStream ois = new ObjectInputStream(bais);

            String readString = (String) ois.readObject();

            assertEquals( stringTest, readString);
        }
    }

मुझे Character.digit समाधान पसंद है, लेकिन यहां मैंने इसे हल किया है

public byte[] hex2ByteArray( String hexString ) {
    String hexVal = "0123456789ABCDEF";
    byte[] out = new byte[hexString.length() / 2];

    int n = hexString.length();

    for( int i = 0; i < n; i += 2 ) {
        //make a bit representation in an int of the hex value 
        int hn = hexVal.indexOf( hexString.charAt( i ) );
        int ln = hexVal.indexOf( hexString.charAt( i + 1 ) );

        //now just shift the high order nibble and add them together
        out[i/2] = (byte)( ( hn << 4 ) | ln );
    }

    return out;
}

मुझे लगता है कि यह आपके लिए करेगा। मैंने इसे एक समान फ़ंक्शन से एक साथ दबाया जिसने डेटा को एक स्ट्रिंग के रूप में वापस कर दिया:

private static byte[] decode(String encoded) {
    byte result[] = new byte[encoded/2];
    char enc[] = encoded.toUpperCase().toCharArray();
    StringBuffer curr;
    for (int i = 0; i < enc.length; i += 2) {
        curr = new StringBuffer("");
        curr.append(String.valueOf(enc[i]));
        curr.append(String.valueOf(enc[i + 1]));
        result[i] = (byte) Integer.parseInt(curr.toString(), 16);
    }
    return result;
}

मेरा औपचारिक समाधान:

/**
 * Decodes a hexadecimally encoded binary string.
 * <p>
 * Note that this function does <em>NOT</em> convert a hexadecimal number to a
 * binary number.
 *
 * @param hex Hexadecimal representation of data.
 * @return The byte[] representation of the given data.
 * @throws NumberFormatException If the hexadecimal input string is of odd
 * length or invalid hexadecimal string.
 */
public static byte[] hex2bin(String hex) throws NumberFormatException {
    if (hex.length() % 2 > 0) {
        throw new NumberFormatException("Hexadecimal input string must have an even length.");
    }
    byte[] r = new byte[hex.length() / 2];
    for (int i = hex.length(); i > 0;) {
        r[i / 2 - 1] = (byte) (digit(hex.charAt(--i)) | (digit(hex.charAt(--i)) << 4));
    }
    return r;
}

private static int digit(char ch) {
    int r = Character.digit(ch, 16);
    if (r < 0) {
        throw new NumberFormatException("Invalid hexadecimal string: " + ch);
    }
    return r;
}

PHP हेक्स 2bin () फ़ंक्शन की तरह है लेकिन जावा शैली में है।

उदाहरण:

String data = new String(hex2bin("6578616d706c65206865782064617461"));
// data value: "example hex data"

मैंने हमेशा एक विधि का उपयोग किया है

public static final byte[] fromHexString(final String s) {
    String[] v = s.split(" ");
    byte[] arr = new byte[v.length];
    int i = 0;
    for(String val: v) {
        arr[i++] =  Integer.decode("0x" + val).byteValue();

    }
    return arr;
}

यह विधि स्पेस सीमांकित हेक्स मानों पर विभाजित होती है लेकिन इसे किसी अन्य मानदंड पर स्ट्रिंग को विभाजित करना मुश्किल नहीं होगा जैसे दो वर्णों के समूह में।


यदि आपके पास कोडिंग शैली के रूप में जावा 8 स्ट्रीम के लिए प्राथमिकता है तो यह केवल जेडीके प्राइमेटिव का उपयोग करके हासिल किया जा सकता है।

String hex = "0001027f80fdfeff";

byte[] converted = IntStream.range(0, hex.length() / 2)
    .map(i -> Character.digit(hex.charAt(i * 2), 16) << 4 | Character.digit(hex.charAt((i * 2) + 1), 16))
    .collect(ByteArrayOutputStream::new,
             ByteArrayOutputStream::write,
             (s1, s2) -> s1.write(s2.toByteArray(), 0, s2.size()))
    .toByteArray();

यदि आप IOException को पकड़ने में कोई फर्क नहीं पड़ता है तो संग्राहक concatenate फ़ंक्शन में , 0, s2.size() पैरामीटर छोड़े जा सकते हैं।


यहां एक समाधान है जो मुझे लगता है कि अब तक किसी भी पोस्ट की तुलना में बेहतर है:

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;
}

इसका कारण क्यों है:

  • अग्रणी शून्य (BigInteger के विपरीत) और नकारात्मक बाइट मानों के साथ सुरक्षित (Byte.parseByte के विपरीत)

  • स्ट्रिंग को एक char[] में परिवर्तित नहीं करता है, या प्रत्येक बाइट के लिए स्ट्रिंगबिल्डर और स्ट्रिंग ऑब्जेक्ट्स नहीं बनाता है।

अगर तर्क सुरक्षित होने के लिए ज्ञात नहीं है तो assert या अपवाद के माध्यम से तर्क जांच जोड़ने के लिए स्वतंत्र महसूस करें।


संपादित करें: जैसा कि @mmyers द्वारा इंगित किया गया है, यह विधि इनपुट पर काम नहीं करती है जिसमें उच्च बिट सेट ("80" - "एफएफ") के साथ बाइट्स से संबंधित सबस्ट्रिंग शामिल हैं। स्पष्टीकरण बग आईडी पर है: 625 9 307 बाइट.परसेबाइट एसडीके दस्तावेज़ीकरण में विज्ञापित के रूप में काम नहीं कर रहा है

public static final byte[] fromHexString(final String s) {
    byte[] arr = new byte[s.length()/2];
    for ( int start = 0; start < s.length(); start += 2 )
    {
        String thisByte = s.substring(start, start+2);
        arr[start/2] = Byte.parseByte(thisByte, 16);
    }
    return arr;
}

एक-लाइनर्स:

import javax.xml.bind.DatatypeConverter;

public static String toHexString(byte[] array) {
    return DatatypeConverter.printHexBinary(array);
}

public static byte[] toByteArray(String s) {
    return DatatypeConverter.parseHexBinary(s);
}

चेतावनी :

  • जावा 9 में आरा यह अब (डिफ़ॉल्ट) java.se रूट सेट का हिस्सा नहीं है, इसलिए इसके परिणामस्वरूप क्लास नॉटफाउंड अपवाद होगा जब तक कि आप निर्दिष्ट नहीं करते --add-modules java.se.ee (@ eckes लिए धन्यवाद)
  • एंड्रॉइड पर उपलब्ध नहीं है (उस पर ध्यान देने के लिए Fabian लिए धन्यवाद), लेकिन अगर किसी कारण से आपके सिस्टम में javax.xml कमी है तो आप केवल स्रोत कोड ले सकते हैं। स्रोत निकालने के लिए @ Bert Regelink लिए धन्यवाद।

public static byte[] hex2ba(String sHex) throws Hex2baException {
    if (1==sHex.length()%2) {
        throw(new Hex2baException("Hex string need even number of chars"));
    }

    byte[] ba = new byte[sHex.length()/2];
    for (int i=0;i<sHex.length()/2;i++) {
        ba[i] = (Integer.decode(
                "0x"+sHex.substring(i*2, (i+1)*2))).byteValue();
    }
    return ba;
}





dump