ass*_*ias 8 java list process character-encoding
这篇文章提供了一个解决方案来检索Windows下正在运行的进程列表.实质上它确实:
String cmd = System.getenv("windir") + "\\system32\\" + "tasklist.exe";
Process p = Runtime.getRuntime().exec(cmd);
InputStreamReader isr = new InputStreamReader(p.getInputStream());
BufferedReader input = new BufferedReader(isr);
Run Code Online (Sandbox Code Playgroud)
然后读取输入.
它看起来和工作得很好,但我想知道任务列表使用的字符集是否可能不是默认的字符集,并且此调用可能会失败?
例如,关于不同可执行文件的另一个问题表明它可能会导致一些问题.
如果是这种情况,有没有办法确定适当的字符集是什么?
Gle*_*est 12
可以将其分为两部分:
windows部分
从java开始执行Windows命令 - 外部到"Windows land"中的jvm.当java Runtime类执行一个windows命令时,它使用DLL作为控制台,因此对Windows来说就像命令在控制台中运行一样Q:当我在控制台中
运行C:\ windows\system32\tasklist.exe时,是什么结果的字符编码(Windows术语中的"代码页")?
java部分:
如何解码来自windows代码页"x"的java字节流(例如850或1252)?
完整解决方案
String cmd = System.getenv("windir") + "\\system32\\" + "chcp.com";
Process p = Runtime.getRuntime().exec(cmd);
// Use default charset here - only want digits which are "core UTF8/UTF16";
// ignore text preceding ":"
String windowsCodePage = new Scanner(
new InputStreamReader(p.getInputStream())).skip(".*:").next();
Charset charset = null;
String[] charsetPrefixes =
new String[] {"","windows-","x-windows-","IBM","x-IBM"};
for (String charsetPrefix : charsetPrefixes) {
try {
charset = Charset.forName(charsetPrefix+windowsCodePage);
break;
} catch (Throwable t) {
}
}
// If no match found, use default charset
if (charset == null) charset = Charset.defaultCharset();
cmd = System.getenv("windir") + "\\system32\\" + "tasklist.exe";
p = Runtime.getRuntime().exec(cmd);
InputStreamReader isr = new InputStreamReader(p.getInputStream(), charset);
BufferedReader input = new BufferedReader(isr);
// Debugging output
System.out.println("matched codepage "+windowsCodePage+" to charset name:"+
charset.name()+" displayName:"+charset.displayName());
String line;
while ((line = input.readLine()) != null) {
System.out.println(line);
}
Run Code Online (Sandbox Code Playgroud)
谢谢Q!- 很好玩.
实际上,所使用的字符集tasklist
是始终从系统默认值不同.
另一方面,只要输出限制为ASCII,就可以非常安全地使用默认值.通常,可执行模块的名称中只包含ASCII字符.
因此,要获得正确的字符串,您必须将(ANSI)Windows代码页转换为OEM代码页,并将后者作为charset传递给InputStreamReader
.
似乎这些编码之间没有全面的映射.可以使用以下映射:
Map<String, String> ansi2oem = new HashMap<String, String>();
ansi2oem.put("windows-1250", "IBM852");
ansi2oem.put("windows-1251", "IBM866");
ansi2oem.put("windows-1252", "IBM850");
ansi2oem.put("windows-1253", "IBM869");
Charset charset = Charset.defaultCharset();
String streamCharset = ansi2oem.get(charset.name());
if (streamCharset) {
streamCharset = charset.name();
}
InputStreamReader isr = new InputStreamReader(p.getInputStream(),
streamCharset);
Run Code Online (Sandbox Code Playgroud)
这种方法适合我windows-1251
和IBM866
配对.
要获取Windows使用的当前OEM编码,您可以使用GetOEMCP
函数.返回值取决于" 区域和语言"控制面板中" 管理"选项卡上的非Unicode程序的语言设置.需要重新启动才能应用更改.
前者由在GUI模式下运行的非Unicode应用程序使用.
后者由控制台应用程序使用.控制台应用程序无法显示无法在当前OEM编码中表示的字符.
由于tasklist
是控制台模式应用程序,其输出始终采用当前的OEM编码.
对于英语系统,该对通常是Windows-1252和CP850.
因为我在俄罗斯,我的系统有以下编码:Windows-1251和CP866.
如果我将输出捕获tasklist
到文件中,则该文件无法正确显示西里尔字符:
在记事本中查看时,我得到的
??????
是??????
(嗨!).
并µTorrent
显示为?Torrent
.
您无法更改使用的编码tasklist
.
但是,可以更改输出编码cmd
.如果您将/u
开关传递给它,它将以UTF-16编码输出所有内容.
cmd /c echo Hi>echo.txt
Run Code Online (Sandbox Code Playgroud)
大小echo.txt
为4个字节:Hi
两个字节用于新行(\r
和\n
)的两个字节.
cmd /u /c echo Hi>echo.txt
Run Code Online (Sandbox Code Playgroud)
现在大小echo.txt
为8个字节:每个字符用两个字节表示.