Java 以字符集安全的方式获取Windows上的进程列表
提供在Windows下检索正在运行的进程列表的解决方案。实质上,它是:Java 以字符集安全的方式获取Windows上的进程列表,java,list,character-encoding,process,Java,List,Character Encoding,Process,提供在Windows下检索正在运行的进程列表的解决方案。实质上,它是: String cmd = System.getenv("windir") + "\\system32\\" + "tasklist.exe"; Process p = Runtime.getRuntime().exec(cmd); InputStreamReader isr = new InputStreamReader(p.getInputStream()); BufferedReader input = new Buff
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);
然后读取输入
它看起来很好,工作也很好,但我想知道任务列表使用的字符集是否可能不是默认字符集,并且此调用是否会失败
例如,它可能会导致一些问题
如果是这样的话,有没有一种方法可以确定合适的字符集是什么?有一种更好的方法可以检查正在运行的进程,甚至可以通过java:and运行OS命令 至于字符集,您可以随时向操作系统查询支持的字符集,并根据需要获取or [编辑] 让我们把它分解;无法知道给定字符串的字节是以何种方式编码的,因此您唯一的选择是获取这些字节,根据需要改变顺序(如果您遇到这样的环境,进程可以以不同的顺序为您提供字节数组,请使用ByteBuffer来处理),并使用支持的多个字符集解码器将字节解码为合理的输出 这太过分了,需要您估计给定的输出可能是UTF-8、UTF-16或任何其他编码。但至少可以使用其中一个可能的字符集对给定的输出进行解码,然后尝试根据需要使用处理后的输出
由于我们讨论的是一个由JVM本身运行的同一操作系统运行的进程,因此您的输出很可能是availableCharsets()方法返回的一个字符集编码。实际上,
任务列表
使用的字符集总是与系统默认值不同
另一方面,只要输出限制为,使用默认值是非常安全的。通常可执行模块的名称中只有ASCII字符
因此,要获得正确的字符串,必须将(ANSI)Windows代码页转换为OEM代码页,并将后者作为字符集传递给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);
echo.txt
的大小为4个字节:两个字节用于Hi
,两个字节用于新行(\r
和\n
)
现在,echo.txt的大小是8个字节:每个字符用两个字节表示。为什么不通过使用Windows API,而不是生成进程?像这样:
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Tlhelp32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.win32.W32APIOptions;
import com.sun.jna.Native;
public class ListProcesses {
public static void main(String[] args) {
Kernel32 kernel32 = (Kernel32) Native.loadLibrary(Kernel32.class, W32APIOptions.UNICODE_OPTIONS);
Tlhelp32.PROCESSENTRY32.ByReference processEntry = new Tlhelp32.PROCESSENTRY32.ByReference();
WinNT.HANDLE snapshot = kernel32.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0));
try {
while (kernel32.Process32Next(snapshot, processEntry)) {
System.out.println(processEntry.th32ProcessID + "\t" + Native.toString(processEntry.szExeFile));
}
}
finally {
kernel32.CloseHandle(snapshot);
}
}
}
我发布了一个类似的答案。可以将其分为两部分:
从java执行Windows命令-从外部到“Windows land”中的jvm。当java运行时类执行windows命令时,它将DLL用于控制台&因此在windows看来,该命令就像在控制台中运行一样
Q:当我在控制台中运行C:\windows\system32\tasklist.exe时,结果的字符编码(“windows术语中的代码页”)是什么?
- 不带参数的windows“chcp”命令给出控制台的活动代码页码(例如,850表示多语言拉丁语-1,1252表示拉丁语-1)。参见,,
默认的系统代码页最初是根据您的系统语言环境设置的(键入systeminfo可查看此页面或“控制面板”->“区域和语言”) - windows OS/.NET函数也提供了此信息
如何从“x”的windows代码页(例如850或1252)解码java字节流?
- windows代码页码和等效java字符集名称之间的完整映射可以从
- 但是,在实践中,可以添加以下前缀之一以实现映射:
对于ISO为“”(无),对于OEM为“IBM”或“x-IBM”,对于Microsoft/windows为“windows-”或“x-windows-”。
例如ISO-8859-1或IBM850或windows-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);
}
谢谢你的提问很有趣。这里有问题吗?你试过并看到了吗?@JimGarrison我从FindBugs那里得到一个关于InputStreamReader中“依赖默认编码”的警告,我不知道这是否会导致问题。所以我搜索并找到了第二个帖子,似乎说它可以。这就是我想检查的。在我的机器上,这段代码运行良好。我将把它作为注释而不是问题添加,因为我的不确定性相当大。也就是说,我认为这样的系统实用程序使用的字符集应该是OS安装的默认语言环境。查询该语言环境并使用它解释输出流似乎是最通用的方法。但如果也存在本地化,则需要对可能更改的字段进行反向工程,以便将其解析出来。这完全取决于所讨论的实用程序是否是以这种方式编写的。我已经在使用一个进程,我知道如何指定字符集。问题是:使用哪个字符集。您声明“您可以随时向操作系统查询支持的字符集”:您是如何做到这一点的?我如何知道特定程序使用了哪些受支持的字符集?您使用的是进程,而不是ProcessBuilder,这比使用Runtime类更干净。获取可用字符集需要调用的实际方法是Charset.availableCharsets()。但即便如此,使用我给你的javadocs中的方法测试字符集会更安全——CharsetEncoder.canEncode()、detect(),等等……很抱歉,我不明白这是如何工作的。你能举一个简单的例子,说明你将如何将你的建议应用到我的特定用例中吗?对答案进行了一些改进,以解释我对这个问题的观点;现在所涉及的代码应该很容易想象,我可以再举一个简单的例子,它也没有什么用处-首先你需要知道我的建议是否适合你的需要,或者没有
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Tlhelp32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.win32.W32APIOptions;
import com.sun.jna.Native;
public class ListProcesses {
public static void main(String[] args) {
Kernel32 kernel32 = (Kernel32) Native.loadLibrary(Kernel32.class, W32APIOptions.UNICODE_OPTIONS);
Tlhelp32.PROCESSENTRY32.ByReference processEntry = new Tlhelp32.PROCESSENTRY32.ByReference();
WinNT.HANDLE snapshot = kernel32.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0));
try {
while (kernel32.Process32Next(snapshot, processEntry)) {
System.out.println(processEntry.th32ProcessID + "\t" + Native.toString(processEntry.szExeFile));
}
}
finally {
kernel32.CloseHandle(snapshot);
}
}
}
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);
}