问题描述
使用ProcessBuilder
,我需要能够将非ASCII参数发送到另一个Java程序。
在这种情况下,程序Abc
需要发送例如通过参数将Def
的阿拉伯字符编程。我可以控制Abc
代码,但不能控制Def
。
使用ProcessBuilder
的常规方法而不使用任何编码,有人提到here,这是不可能的。 Def
收到问号“ ?????”。
但是,我可以得到一些结果,但是可以将不同的编码用于不同的情况。
例如我正在尝试将所有编码发送给收件人,并比较预期的结果。
- Windows,IntelliJ控制台:
- 默认字符集:
UTF-8
- 找到的字符集:
windows-1252
,windows-1254
和windows-1258
- 默认字符集:
- Windows,命令提示符:
- 默认字符集:
windows-1252
- 找到的字符集:
CESU-8
和UTF-8
- 默认字符集:
- Ubuntu,命令提示符:
- 默认字符集:
ISO-8859-1
- 找到的字符集:
ISO-2022-CN
,ISO-2022-KR
,ISO-8859-1
,ISO-8859-15
,ISO-8859-9
,x-IBM1129
,x-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
该库和此特定功能已被多个用户使用并经过良好测试。
免责声明:这个库是我写的