Netty中用于打印字节流(类似winhex)的工具类及其改进

Netty中类似winhex用于打印字节流的工具类

三年前,本人为了查看java类字节码,使用了一款叫做winhex的软件用于打开.class的二进制文件,当时最大的感觉就是如获至宝。winhex的界面截图如下所示:

Netty中用于打印字节流(类似winhex)的工具类及其改进_第1张图片

时隔一年,本人在阅读Netty源码的时候,竟然发现netty中也存在一个类似winhex的工具类ByteBufUtil。 该类中的方法:*public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf)*可以将ByteBuf对象中的字节流以类似winhex的方式呈现。

然而遗憾的是,ByteBufUtil并不能直接打印字节数组byte[]中的内容,为了弥补这个缺陷,本人参考了ByteBufUtil的源码,并作了少量的修改,实现了打印byte[]数组得目的。 相关代码如下所示:核心代码就是BytesPrinter类的print方法。代码如下:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.util.ByteProcessor;
import org.junit.Test;

import java.io.*;


/**
 * 

* 类说明 *

* * @author lihong10 2019/6/12 19:26 * @version v1.0 * @modificationHistory=========================逻辑或功能性重大变更记录 * @modify by user: {修改人} 2019/6/12 19:26 * @modify by reason:{方法名}:{原因} */
public class BytesPrinter { public static final String NEWLINE = System.getProperty("line.separator", "\n"); private static final char[] BYTE2CHAR = new char[256]; private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4]; private static final String[] HEXPADDING = new String[16]; private static final String[] BYTE2HEX = new String[256]; private static final String[] BYTEPADDING = new String[16]; private static final String[] BYTE2HEX_PAD = new String[256]; private static final String[] BYTE2HEX_NOPAD = new String[256]; static { int i; // Generate the lookup table for hex dump paddings for (i = 0; i < HEXPADDING.length; i ++) { int padding = HEXPADDING.length - i; StringBuilder buf = new StringBuilder(padding * 3); for (int j = 0; j < padding; j ++) { buf.append(" "); } HEXPADDING[i] = buf.toString(); } // Generate the lookup table for the start-offset header in each row (up to 64KiB). for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i ++) { StringBuilder buf = new StringBuilder(12); buf.append(NEWLINE); buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L)); buf.setCharAt(buf.length() - 9, '|'); buf.append('|'); HEXDUMP_ROWPREFIXES[i] = buf.toString(); } // Generate the lookup table for byte-to-hex-dump conversion for (i = 0; i < BYTE2HEX.length; i ++) { BYTE2HEX[i] = ' ' + byteToHexStringPadded(i); } // Generate the lookup table for byte dump paddings for (i = 0; i < BYTEPADDING.length; i ++) { int padding = BYTEPADDING.length - i; StringBuilder buf = new StringBuilder(padding); for (int j = 0; j < padding; j ++) { buf.append(' '); } BYTEPADDING[i] = buf.toString(); } // Generate the lookup table for byte-to-char conversion for (i = 0; i < BYTE2CHAR.length; i ++) { if (i <= 0x1f || i >= 0x7f) { BYTE2CHAR[i] = '.'; } else { BYTE2CHAR[i] = (char) i; } } // Generate the lookup table that converts a byte into a 2-digit hexadecimal integer. for (i = 0; i < BYTE2HEX_PAD.length; i++) { String str = Integer.toHexString(i); BYTE2HEX_PAD[i] = i > 0xf ? str : ('0' + str); BYTE2HEX_NOPAD[i] = str; } } @Test public void printByByteBufUtil() { PooledByteBufAllocator pooledByteBufAllocator = new PooledByteBufAllocator(false); //这里: byteBuf的实际类型是:PooledUnsafeHeapByteBuf ByteBuf byteBuf = pooledByteBufAllocator.heapBuffer(252); System.out.println("has array: " + byteBuf.hasArray()); for (int i = 0; i < 43; i++) { byteBuf.writeInt(i); } //返回不满足条件的首个位置 byte target = 0; System.out.println(byteBuf.writerIndex() + ":" + byteBuf.readerIndex()); int position = byteBuf.forEachByte(new ByteProcessor() { @Override public boolean process(byte value) throws Exception { boolean res = value == target; if(res) { System.out.print(BYTE2HEX[getUnsignedByte(value)] + " "); } return res; } }); System.out.println(); System.out.println("position is: " + position); System.out.println(byteBuf.writerIndex() + ":" + byteBuf.readerIndex()); StringBuilder builder = new StringBuilder(); ByteBufUtil.appendPrettyHexDump(builder, byteBuf); System.out.println(builder); } @Test public void testPrint() { byte[] bytes = "abcdefghijklmnopqrstuvwxyz".getBytes(); StringBuilder builder = new StringBuilder(); print(builder, bytes, 0, bytes.length); System.out.println(builder); } @Test public void testPrintByteArray() { File f = new File("C:\\Users\\lihong10\\Desktop\\Deployer.class"); FileInputStream is = null; try { is = new FileInputStream(f); byte[] bytes = toByteArray(is); StringBuilder builder = new StringBuilder(); print(builder, bytes, 0, bytes.length); System.out.println(builder); } catch (Exception e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } @Test public void testPrintFileStream() { File f = new File("C:\\Users\\lihong10\\Desktop\\Deployer.class"); FileInputStream is = null; try { is = new FileInputStream(f); byte[] bytes = toByteArray(is); PooledByteBufAllocator pooledByteBufAllocator = new PooledByteBufAllocator(false); //这里: byteBuf的实际类型是:PooledUnsafeHeapByteBuf ByteBuf byteBuf = pooledByteBufAllocator.heapBuffer(bytes.length); byteBuf.writeBytes(bytes); StringBuilder builder = new StringBuilder(); ByteBufUtil.appendPrettyHexDump(builder, byteBuf); System.out.println(builder); } catch (Exception e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } public static byte[] toByteArray(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[1024 * 4]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } return output.toByteArray(); } /** * 打印字节数组的关键方法 * @param dump 打印的内容会被追加到dump中 * @param buf 字节数组,被打印的对象 * @param offset 偏移或起始位置 * @param length 打印的字节长度 * @return * @modificationHistory=========================逻辑或功能性重大变更记录 * @modify by user: {修改人} 2019/8/1 19:11 * @modify by reason:{原因} */ private static void print(StringBuilder dump, byte[] buf, int offset, int length) { if (buf.length != length) { throw new IndexOutOfBoundsException("expected len == length"); } if (length == 0) { return; } dump.append( " +-------------------------------------------------+" + NEWLINE + " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |" + NEWLINE + "+--------+-------------------------------------------------+----------------+"); final int startIndex = offset; final int fullRows = length >>> 4; final int remainder = length & 0xF; // Dump the rows which have 16 bytes. for (int row = 0; row < fullRows; row ++) { int rowStartIndex = (row << 4) + startIndex; // Per-row prefix. appendHexDumpRowPrefix(dump, row, rowStartIndex); // Hex dump int rowEndIndex = rowStartIndex + 16; for (int j = rowStartIndex; j < rowEndIndex; j ++) { dump.append(BYTE2HEX[getUnsignedByte(buf[j])]); } dump.append(" |"); // ASCII dump for (int j = rowStartIndex; j < rowEndIndex; j ++) { dump.append(BYTE2CHAR[getUnsignedByte(buf[j])]); } dump.append('|'); } // Dump the last row which has less than 16 bytes. if (remainder != 0) { int rowStartIndex = (fullRows << 4) + startIndex; appendHexDumpRowPrefix(dump, fullRows, rowStartIndex); // Hex dump int rowEndIndex = rowStartIndex + remainder; for (int j = rowStartIndex; j < rowEndIndex; j ++) { dump.append(BYTE2HEX[getUnsignedByte(buf[j])]); } dump.append(HEXPADDING[remainder]); dump.append(" |"); // Ascii dump for (int j = rowStartIndex; j < rowEndIndex; j ++) { dump.append(BYTE2CHAR[getUnsignedByte(buf[j])]); } dump.append(BYTEPADDING[remainder]); dump.append('|'); } dump.append(NEWLINE + "+--------+-------------------------------------------------+----------------+"); } private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) { if (row < HEXDUMP_ROWPREFIXES.length) { dump.append(HEXDUMP_ROWPREFIXES[row]); } else { dump.append(NEWLINE); dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L)); dump.setCharAt(dump.length() - 9, '|'); dump.append('|'); } } public static String byteToHexStringPadded(int value) { return BYTE2HEX_PAD[value & 0xff]; } /** * 字节转换成无符号数 * @param b * @return * @modificationHistory=========================逻辑或功能性重大变更记录 * @modify by user: {修改人} 2019/8/1 19:13 * @modify by reason:{原因} */ private static short getUnsignedByte(byte b) { return (short)(b & 0xFF); } @Test public void toBinaryString() { System.out.println(Int2BinaryStringUtil.toFullBinaryString(0xFFFFFFFFL)); System.out.println(Int2BinaryStringUtil.toFullBinaryString(0x100000000L)); System.out.println(Int2BinaryStringUtil.toFullBinaryString(0L)); System.out.println("\n\r"); System.out.println(Int2BinaryStringUtil.toFullBinaryString(0 & 0xFFFFFFFFL | 0x100000000L)); System.out.println(Long.toHexString(0 & 0xFFFFFFFFL | 0x100000000L)); System.out.println("\n\r"); System.out.println(1 << 4); System.out.println(Int2BinaryStringUtil.toFullBinaryString(16 & 0xFFFFFFFFL | 0x100000000L)); System.out.println(Long.toHexString(16 & 0xFFFFFFFFL | 0x100000000L)); } }

上述代码中用到得辅助类Int2BinaryStringUtil代码如下:


/**
 * 该类是一个将int型整数转成32位二进制字符串形式的工具类
 * Created by lihong10 on 2017/4/28.
 */
public class Int2BinaryStringUtil {


    private final static char[] digits = {'0', '1'};


    /**
     * 将int型整数转成32位的2进制形式
     * @param num
     * @return String
     */
    public static String toFullBinaryString(int num, boolean indent)
    {
        char[] buf = new char[32];
        int pos = 32;
        int mask = 1;
        do
        {
            buf[--pos] = digits[num & mask];
            num >>>= 1;
        }
        while (pos > 0);
        if (indent) {
            return formatString(buf);
        }

        return new String(buf, pos, 32);
    }

    public static String toFullBinaryString(int num)
    {
        char[] buf = new char[32];
        int pos = 32;
        int mask = 1;
        do
        {
            buf[--pos] = digits[num & mask];
            num >>>= 1;
        }
        while (pos > 0);

        return new String(buf, pos, 32);
    }


    public static String toFullBinaryString(long num)
    {
        char[] buf = new char[64];
        int pos = 64;
        int mask = 1;
        do
        {
            buf[--pos] = digits[(int)(num & mask)];
            num >>>= 1;
        }
        while (pos > 0);

        return new String(buf, pos, 64);
    }

    public static String toFullBinaryString(long num, boolean indent)
    {
        char[] buf = new char[64];
        int pos = 64;
        int mask = 1;
        do
        {
            buf[--pos] = digits[(int)(num & mask)];
            num >>>= 1;
        }
        while (pos > 0);

        if (indent) {
            return  formatString(buf);
        }

        return new String(buf, pos, 64);
    }


    public static String formatString(char[] buf) {

        //从后往前,每相隔8个元素就插入一个空格,一共插入8个空格
        char[] formatBuf = new char[buf.length + (buf.length / 8)];
        int k = formatBuf.length;
        for(int i = buf.length; i > 0; i--) {
            formatBuf[--k] = buf[i - 1];
            if ((i - 1) % 8 == 0) {
                //插入空格
                formatBuf[--k] = (char) 0;
            }
        }
        return new String(formatBuf, 1, formatBuf.length - 1);

    }



    public static String toFullBinaryString(byte num)
    {
        num &= 0xFF;
        char[] buf = new char[8];
        int pos = 8;
        int mask = 1;
        do
        {
            buf[--pos] = digits[num & mask];
            num >>>= 1;
        }
        while (pos > 0);

        return new String(buf, pos, 8);
    }

    public static String toFullBinaryString(char num)
    {
        num &= 0xFFFF;
        char[] buf = new char[16];
        int pos = 16;
        int mask = 1;
        do
        {
            buf[--pos] = digits[num & mask];
            num >>>= 1;
        }
        while (pos > 0);

        return new String(buf, pos, 16);
    }


    /**
     * 将int型整数转成32位的2进制形式
     * @param num
     * @return String
     */
    public static String toFullBinaryString2(int num)
    {
        char[] chs = new char[Integer.SIZE];
        for (int i = 0; i < Integer.SIZE; i++)
        {
            chs[Integer.SIZE - 1 - i] = (char) ((num >> i & 1) + '0');
        }
        return new String(chs);
    }

    public static void printComplementCode(int a)
    {
        for (int i = 0; i < 32; i++)
        {
            // 0x80000000 是一个首位为1,其余位数为0的整数
            int t = (a & 0x80000000 >>> i) >>> (31 - i);
            System.out.print(t);
        }
        System.out.println();
    }




    /**
     * 测试
     * @param args
     */
    public static void main(String[] args)
    {
       /* System.out.println("方法一:" + toFullBinaryString(6053));
        System.out.println("JDK自带: " + Integer.toBinaryString(6053));

        System.out.println("方法一:" + toFullBinaryString(-2));
        System.out.println("JDK自带: " + Integer.toBinaryString(-2));

        System.out.println("------------------------------------------");

        System.out.println("方法二:" + toFullBinaryString2(6053));
        System.out.println("JDK自带: " + Integer.toBinaryString(6053));

        System.out.println("方法二:" + toFullBinaryString2(-2));
        System.out.println("JDK自带: " + Integer.toBinaryString(-2));*/

        System.out.println("方法一:" + toFullBinaryString(255));
//        System.out.println("JDK自带: " + Integer.toBinaryString(6053));


        System.out.println(toFullBinaryString('\uffff'));
        System.out.println(toFullBinaryString(0xFFFF));
        System.out.println(toFullBinaryString(0x80000000 ));

        printComplementCode(-10); // 11111111 11111111 11111111 11110110
        printComplementCode(10); // 11111111 11111111 11111111 11110110


        System.out.println(toFullBinaryString(10));
        System.out.println(toFullBinaryString(Integer.reverseBytes(10)));


        System.out.println(toFullBinaryString(1));
        System.out.println(toFullBinaryString(Integer.reverse(1)));
        System.out.println(Integer.reverse(1));


    }

}

你可能感兴趣的:(netty,netty,winhex,打印字节数组中的内容)