ProcessBuilder参数的哪种编码

问题描述

使用ProcessBuilder,我需要能够将非ASCII参数发送到另一个Java程序。

在这种情况下,程序Abc需要发送例如通过参数将Def的阿拉伯字符编程。我可以控制Abc代码,但不能控制Def

使用ProcessBuilder的常规方法而不使用任何编码,有人提到here,这是不可能的。 Def收到问号“ ?????”。

但是,我可以得到一些结果,但是可以将不同的编码用于不同的情况。

例如我正在尝试将所有编码发送给收件人,并比较预期的结果。

  1. Windows,IntelliJ控制台:
    1. 认字符集:UTF-8
    2. 找到的字符集:windows-1252windows-1254windows-1258
  2. Windows,命令提示符:
    1. 认字符集:windows-1252
    2. 找到的字符集:CESU-8UTF-8
  3. Ubuntu,命令提示符:
    1. 认字符集:ISO-8859-1
    2. 找到的字符集:ISO-2022-CNISO-2022-KRISO-8859-1ISO-8859-15ISO-8859-9x-IBM1129x-ISO-2022-CN-CNS和{{ 1}}

我的问题是:由于我需要通用的东西,如何以编程方式知道要使用哪种正确编码?

换句话说,认字符集和找到的字符集之间是什么关系?

x-ISO-2022-CN-GB

public class Abc {

  private static final Path PATH = Paths.get("."); // With maven: ./target/classes

  public static void main(String[] args) throws Exception {
    var string = "hello أحمد";
    var bytes = string.getBytes();

    System.out.println("Original string: " + string);
    System.out.println("Default charset: " + Charset.defaultCharset());

    for (var c : Charset.availableCharsets().values()) {
      var newString = new String(bytes,c);

      var process = new ProcessBuilder().command("java","-cp",PATH.toAbsolutePath().toString(),"Def",newString).start();
      process.waitFor();

      var output = asstring(process.getInputStream());
      if (output.contains(string)) {
        System.out.println("Found " + c + " " + output);
      }
    }
  }

  private static String asstring(InputStream is) throws IOException {
    try (var reader = new BufferedReader(new InputStreamReader(is))) {
      var builder = new StringBuilder();
      String line;
      while ((line = reader.readLine()) != null) {
        if (builder.length() != 0) {
          builder.append(System.lineseparator());
        }
        builder.append(line);
      }
      return builder.toString();
    }
  }
}

解决方法

在后台,实际上传递的是字节,而不是char。通常,您希望最终将字符转换为字节的java方法具有重载功能,可让您指定字符集,但是由于某种原因,它在这里不存在。

应该的工作方式如下:

  • 您将字符串传递给ProcessBuilder
  • PB将使用Charset.defaultCharset()将该字符串转换为字节(为什么?因为PB只是为了使操作系统执行操作,并且默认字符集反映了操作系统的首选字符集)。
  • 然后将这些字节提供给进程。
  • 该过程开始。如果它是Java,并且我们在psv main(String[] args)中讨论args,则相反地做同样的事情:Java再次通过Charset.defaultCharset()来获取字节并将其转回字符。

这确实显示出一个紧迫的问题:如果默认字符集不能表示某个字符,那么从理论上讲,您不走运

强烈建议使用Java来启动java.exe通常意味着您可以传递所需的任何内容(除非所涉及的字符在系统的字符集中无法表示)。

您的代码是奇怪的。特别是这一行是问题所在:

var bytes = string.getBytes();

这是string.getBytes(Charset.defaultCharset())的缩写。现在,您可以在提供的字符集中找到字节了。

var newString = new String(bytes,c);

,现在您要获取这些字节,并使用完全不同的字符集将它们转换为字符串。我不确定您要如何完成此任务。纯粹的傻瓜会出来。

换句话说,默认字符集和找到的字符集之间是什么关系?

“发现的人”是什么意思?字符串“ Found charsets”在代码中无处显示。如果您的意思是:Charset.availableCharsets()返回什么-根本没有关系。 availableCharsets与ProcessBuilder无关。

,

一种可能性是将您的String转换为Unicode序列字符串,然后将其传递给另一个进程,然后将其转换回常规String。 Unicode序列的字符串将始终仅包含ASCI字符。这可能是这样的:

String encoded = StringUnicodeEncoderDecoder.encodeStringToUnicodeSequence("hello أحمد"));

结果将是String编码将保留以下值:

"\u0068\u0065\u006c\u006c\u006f\u0020\u0623\u062d\u0645\u062f"

您可以安全地将此字符串传递给另一个进程。在其他过程中,您可以执行以下操作:

String originalString = StringUnicodeEncoderDecoder.decodeUnicodeSequenceToString(encodedString);

结果将是originalString现在将保留此值:

"hello أحمد"

StringUnicodeEncoderDecoder可在名为MgntUtils的开源库中找到。您可以将这个库作为Maven Artifact或在Github上获取(包括源代码和JavaDoc)。在线JavaDoc可用here

该库和此特定功能已被多个用户使用并经过良好测试。
免责声明:这个库是我写的

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...