Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 以字符集安全的方式获取Windows上的进程列表_Java_List_Character Encoding_Process - Fatal编程技术网

Java 以字符集安全的方式获取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

提供在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);
然后读取输入

它看起来很好,工作也很好,但我想知道任务列表使用的字符集是否可能不是默认字符集,并且此调用是否会失败

例如,它可能会导致一些问题


如果是这样的话,有没有一种方法可以确定合适的字符集是什么?

有一种更好的方法可以检查正在运行的进程,甚至可以通过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);
        }
    } 
}

我发布了一个类似的答案。

可以将其分为两部分:

  • windows部件
    从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函数也提供了此信息

  • java部分:
    如何从“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);
        }