【问题标题】:How do you "print" an array of bytes that handles non-printable values你如何“打印”一个处理不可打印值的字节数组
【发布时间】:2018-10-17 14:30:02
【问题描述】:

我在这个论坛上看到了很多相关的问题和答案,但没有找到任何可以解决我问题的东西。

这个想法很简单:

(这是使用 Java 编程语言 - 我目前仅限使用 Java 7)

接收到一个字节数组(表示任何内容:某种有线格式、某种形式的编码数据、带有嵌入“文本”的二进制数据等)。我希望能够以以下形式打印数组:

  1. 十六进制字符串
  2. 作为“可打印”文本

第一种情况部分是出于调试原因,但也可能在非调试模式下使用。第二种情况纯粹是出于调试原因,并且允许与其他信息源进行人工比较。

我认为我对第一种情况有解决方案,但我正在为第二种情况苦苦挣扎。

显然,一个字节数组可能包含不可打印的字符或以不同方式呈现的字符。如何“打印”一个数组以便表示所有字节?我可以提供的一个很好的模拟是 UNIX od 命令,其中od -ah 以十六进制形式显示二进制数据以及 ASCII 字符。在这种情况下,点 (.) 通常用于代替不可打印或控制字符。

我不需要它看起来像od 输出的那样,但希望能够显示数据,以便至少可以看到可打印的字符,其余的用某种占位符表示。另外,我不想删除不可打印的字符,因为这会误导数据。

如果有人知道如何实现这一点,我将不胜感激。

【问题讨论】:

  • 1) 只需将字节打印为十六进制 2) 使用 new String(byte[]) 创建字符串 - 定义不可打印字符并预先替换它们
  • @Antoniossss 但是new String(byte[]) 使用用户的默认字符编码,所以要执行替换步骤,您必须弄清楚它是什么,并知道该字符集的哪些字符是不可打印的.对于推测性调试转储来说似乎有点复杂。
  • 我会使用 UTF-8,因为无论如何你都不关心编码

标签: java arrays string representation


【解决方案1】:

为什么不使用DatatypeConverter 这里是一个有两个数组的例子。第一个来自单词'hello',第二个是发明的。

import java.nio.charset.StandardCharsets;
import javax.xml.bind.DatatypeConverter;

public class MainTest {

    public static void main(String[] args) {
        // Something printable
        String hello = "hello";
        byte[] printable = hello.getBytes(StandardCharsets.UTF_8); 
        System.out.println("Printable: " + new String(printable, StandardCharsets.UTF_8));

        // Something not printable
        byte[] notPrintable = { (byte)-156, (byte)190, (byte)56, (byte)-29, (byte)1};

        System.out.println("NotPrintable: " + new String(notPrintable, StandardCharsets.UTF_8));

        // With DatatypeConverter.printBase64Binary you can get a String printable from any byte[]
        final String printableString = DatatypeConverter.printBase64Binary(printable);
        final String notPrintableString = DatatypeConverter.printBase64Binary(notPrintable);

        System.out.println("Printable string with DatatypeConverter: " + printableString);
        System.out.println("Not printable string with DatatypeConverter: " + notPrintableString);

        byte[] otherPrintable = DatatypeConverter.parseBase64Binary(printableString);
        byte[] otherNotPrintable = DatatypeConverter.parseBase64Binary(notPrintableString);

        // And the best point is that you can revert it
        final String otherHello = new String(otherPrintable, StandardCharsets.UTF_8);
        final String strangeString = new String(otherNotPrintable, StandardCharsets.UTF_8);

        System.out.println("Other Hello: " + otherHello);
        System.out.println("Strange String: " + strangeString);
    }

}

这是输出

Printable: hello
NotPrintable: d�8�
Printable string with DatatypeConverter: aGVsbG8=
Not printable string with DatatypeConverter: ZL444wE=
Other Hello: hello
Strange String: d�8�

编辑:DatatypeConverter 对 java7 和 java8 有效,看看Java 11 package javax.xml.bind does not exist

【讨论】:

    【解决方案2】:

    嗯,Arrays.toString() 对你有用吗?

    public class PrintBytes {
       public static void main( String[] args ) {
          byte[] test = { 1, 2, 3, 0, (byte)0xFF, (byte)0xFE };
          String s = Arrays.toString( test );
          System.out.println( s );
    
       }
    }
    

    输出:

    run:
    [1, 2, 3, 0, -1, -2]
    BUILD SUCCESSFUL (total time: 0 seconds)
    

    它不是十六进制,但它是一种可用的有线格式。

    【讨论】:

      【解决方案3】:

      #2 试试这个:

      String original = new String(bytes);
      String printable = original.replaceAll("\\P{Print}", ".")); //Or any other character instead of "." you want
      

      它使用POSIX printable character class

      如果您需要 Unicode 支持,请使用 utf-8 字符集参数创建字符串,并使用 UNICODE_CHARACTER_CLASS 标志构造一个模式,并使用与上述相同的正则表达式。

      【讨论】:

      • 我认为 ASCII 表示应该足以满足我正在做的事情。话虽如此,我愿意使用最有意义的任何编码。我承认我对字符编码知之甚少。我还需要研究您的 RE 示例,因为我不知道它的作用。
      • @JosephGagnon 如果 ASCII 可以,您可以按原样使用我的代码 sn-p。
      • 是的,成功了。谢谢。我也尝试过使用 UTF-8,但我一定做错了什么——它显示的输出几乎相同,但表现得很奇怪。
      猜你喜欢
      • 2021-11-29
      • 2011-01-07
      • 1970-01-01
      • 2021-07-05
      • 1970-01-01
      • 2017-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多