为什么UTF8编码不能用于处理输入流中的特殊字符?

2022-01-28 12:36:33 标签 javaencoding

我得到了我的最后一个问题标记为重复的问题,Process。getInputStream()使用什么编码?其实我问的不是这个。在我的第二个示例中,UTF-8可以成功地解析这个特殊字符。但是,当从流程输入流读取特殊字符时,UTF-8就不能正确地解析它了。为什么会这样?这是否意味着ISO_8859_1是我唯一可以选择的选项?

我正在开发一个插件,它可以在运行时检索Azure密钥库的秘密。然而,这里有一个编码问题。我存储的字符串包含特殊字符?字符串如下:HrIaMFBc78!?%$timodagetwi??99。然而与下列程序的特殊字符?无法正确解析:

package com.buildingblocks.azure.cli;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class Test {
    static String decodeText(String command) throws IOException, InterruptedException {
        Process p;
        StringBuilder output = new StringBuilder();
        p = Runtime.getRuntime().exec("cmd.exe /c \"" + command + "\"");
        p.waitFor();
        InputStream stream;
        if (p.exitValue() != 0) {
            stream = p.getErrorStream();
        } else {
            stream = p.getInputStream();
        }
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
        String line = "";
        while ((line = reader.readLine()) != null) {
            output.append(line + "\n");
        }
        return output.toString();
    }
    public static void main(String[] arg) throws IOException, InterruptedException {
        System.out.println(decodeText("az keyvault secret show --name \"test-password\" --vault-name \"test-keyvault\""));
    }
}

输出结果为“value”:“HrIaMFBc78!?%$timodagetwi??”99”

如果我使用下面的程序来解析字符串的特殊字符?可以成功解析。

package com.buildingblocks.azure.cli;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class Test {
    static String decodeText(String input, String encoding) throws IOException {
        return
                new BufferedReader(
                        new InputStreamReader(
                                new ByteArrayInputStream(input.getBytes()),
                                Charset.forName(encoding)))
                        .readLine();
    }
    public static void main(String[] arg) throws IOException {
        System.out.println(decodeText("HrIaMFBc78!?%$timodagetwi??99", StandardCharsets.UTF_8.toString()));
    }
}

它们都使用具有相同设置的BufferedReader,但解析进程输出的那个失败了。有人知道这是为什么吗?

###和你一起阅读UTF-8

您的第二个示例确实将String编写为UTF-8,这样它就可以用前面提到的代码读取,并且工作得很好。

 BufferedReader reader = new BufferedReader(
        new InputStreamReader(stream, StandardCharsets.UTF_8));
       

但是您的第一个示例确实执行cmd。exe(所以Windows操作系统),并通过操作系统获取返回的流数据。

在Windows上,你通常有

作为默认字符集,而不是CP1252 as default charset which is .UTF-8.

你可以将Windows的默认字符编码设置为UTF-8,请看

使用cmd。exe将文本文件保存为UTF-8编码。

或者你只是使用你的操作系统的系统编码(在Windows通常

)在你的输入流阅读器创建(相反CP1252) at your input stream reader creation (instead StandardCharse)。StandardCharsets.UTF_8).

# # # ?in有两个字节的UTF-8编码,所以其中两个是四个字节。两个占位字符?说明只有两个字节。在ISO 8859-1编码a ?有一个字节,这表明编码不是UTF-8,但可能是ISO 8859-1。

InputStream不使用任何编码,它只传输字节。编码在InputStreamReader中使用。

输入的十六进制转储可能有用。或者,您可以尝试在Java程序和您想调用的程序之间插入一个脚本,并分析那里的情况。或者干脆试试ISO 8859-1。

###你在Java中选择的字符集应该匹配你执行的命令使用的编码。它不是UTF-8,可能是ISO-8859-1。因为该命令所使用的编码在不同的机器上很可能默认为不同的值,所以在执行命令之前,您可以尝试将其显式设置为一个已知值:

或者在你的环境中:

chcp 65001 && <command>

Windows代码页65001是UTF-8。

Runtime.getRuntime().exec("cmd.exe /c \"chcp && " + command + "\"");

请注意,如果不能使用子进程的输出,可能会导致它阻塞,并且永远不会终止,因此waitFor()可能会阻塞,因为您随后使用了输出。进程的标准输出可能有足够大的缓冲区来完成,但如果有标准错误的输出,则更有可能阻塞。另一种方法是将标准错误指向父Java进程的stderr。

###你启动的CMD。EXE

将发送一个默认平台字符集的流。这不一定是UTF-8格式,也不一定与JVM默认字符集相同(因为您可能已经用系统属性更改了字符集)ProcessBuilder / Runtime.getRuntime/Runtime.getRuntime wi)。-Dfile.encoding=XYZ).

您可以通过运行CMD。EXE并查看file的值来确定用于第一种方法的CMD。EXE流的字符集。当运行JVM时,不带额外参数就会打印编码:

C:\> java -XshowSettings:properties
Property settings:
...
file.encoding = Cp1252    (or whatever)
阅读全文

▼ 版权说明

相关文章也很精彩
推荐内容
更多标签
相关热门
全站排行
随便看看

错说 cuoshuo.com —— 程序员的报错记录

部分内容根据CC版权协议转载;网站内容仅供参考,生产环境使用务必查阅官方文档

辽ICP备19011660号-5

×

扫码关注公众号:职场神器
发送: 1
获取永久解锁本站全部文章的验证码