问题 将命令行unicode参数传递给Java代码


我必须将日语命令行参数传递给Java main方法。如果我在命令行窗口中键入Unicode字符,则会显示“?????”哪个没问题,但传递给java程序的值也是'?????'。如何获取命令窗口传递的参数的正确值?下面是示例程序,它将命令行参数提供的值写入文件。

public static void main(String[] args) {
        String input = args[0];
        try {
            String filePath = "C:/Temp/abc.txt";
            File file = new File(filePath);
            OutputStream out = new FileOutputStream(file);
            byte buf[] = new byte[1024];
            int len;
            InputStream is = new ByteArrayInputStream(input.getBytes());
            while ((len = is.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            out.close();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3467
2017-10-05 11:34


起源

更改控制台窗口的字符集时是否会更改?哪个操作系统? - Andreas
更改控制台窗口的charset没有帮助。我正在使用Windows 2000 - Pankaj Agrawal


答案:


遗憾的是,您无法可靠地将非ASCII字符与使用Windows C运行时的stdlib的命令行应用程序一起使用,例如Java(以及几乎所有非Windows特定的脚本语言)。

这是因为默认情况下,他们使用特定于语言环境的代码页读取输入和输出,这不是UTF,与使用UTF-8的其他所有现代操作系统不同。

您可以使用。将终端的代码页更改为其他内容 chcp 命令,支持UTF-8编码 chcp 65001 以某种方式打破了可能导致应用程序致命的应用程序。

如果您只需要日语,则可以通过将区域设置(“区域设置”中的“非Unicode应用程序的语言”)设置为日本来切换到代码页932(类似于Shift-JIS)。但是,对于不在该代码页中的字符,这仍然会失败。

如果需要在Windows上通过命令行可靠地获取非ASCII字符,则需要调用Win32 API函数 GetCommandLineW 直接避免编码到系统代码页面层。可能你想用JNA做到这一点。


11
2017-10-05 12:20





遗憾的是,标准Java启动程序在Windows上处理Unicode命令行参数时存在已知且长期存在的错误。也许在其他一些平台上。对于Java 7更新1,它仍然存在。

如果你擅长用C / C ++编程,你可以尝试编写自己的启动器。一些专门的发射器可能不是什么大问题...只需看看最初的例子 JNI调用API 页。

另一种可能性是使用Java包装器和临时文件的组合将Unicode参数传递给Java应用程序。看我的博客 Java,Xalan,Unicode命令行参数...... 更多评论和包装代码。


5
2018-01-28 08:16





问题是因为您的系统区域设置。将您的语言环境更改为日语,它会起作用。

这是如何做到这一点 http://www.java.com/en/download/help/locale.xml


0
2017-10-05 11:38



我们不应该在不改变系统区域设置的情况下传递任何Unicode值,无论是日语还是韩语?现在没有资源去做,会给它一个机会。 - Pankaj Agrawal
这只是单一语言的解决方法。如果一个人在他/她的电脑上有超过1种非英语语言怎么办?如果其他应用程序(如记事本)可以处理非英文字母,那么java应用程序也必须能够在不更改系统区域设置的情况下执行此操作。见下面的答案 stackoverflow.com/a/41923480/285060 这不需要更改操作系统区域设置 - Sergey Karpushin


您可以使用JNA来获取它,这是我的代码中的复制粘贴:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;

public class OsNativeWindowsImpl implements OsNative {
    private static Logger log = Logger.getLogger(OsNativeWindowsImpl.class);

    private Kernel32 kernel32;
    private Shell32 shell32;

    /**
     * This method will try to solve issue when java executable cannot transfer
     * argument in utf encoding. cyrillic languages screws up and application
     * receives ??????? instead of real text
     */
    @Override
    public String[] getCommandLineArguments(String[] fallBackTo) {
        try {
            log.debug("In case we fail fallback would happen to: " + Arrays.toString(fallBackTo));
            String[] ret = getFullCommandLine();
            log.debug("According to Windows API programm was started with arguments: " + Arrays.toString(ret));

            List<String> argsOnly = null;
            for (int i = 0; i < ret.length; i++) {
                if (argsOnly != null) {
                    argsOnly.add(ret[i]);
                } else if (ret[i].toLowerCase().endsWith(".jar")) {
                    argsOnly = new ArrayList<>();
                }
            }
            if (argsOnly != null) {
                ret = argsOnly.toArray(new String[0]);
            }

            log.debug("These arguments will be used: " + Arrays.toString(ret));
            return ret;
        } catch (Throwable t) {
            log.error("Failed to use JNA to get current program command line arguments", t);
            return fallBackTo;
        }
    }

    private String[] getFullCommandLine() {
        try {
            // int pid = kernel32.GetCurrentProcessId();
            IntByReference argc = new IntByReference();
            Pointer argv_ptr = getShell32().CommandLineToArgvW(getKernel32().GetCommandLineW(), argc);
            String[] argv = argv_ptr.getWideStringArray(0, argc.getValue());
            getKernel32().LocalFree(argv_ptr);
            return argv;
        } catch (Throwable t) {
            throw new RuntimeException("Failed to get program arguments using JNA", t);
        }
    }

    private Kernel32 getKernel32() {
        if (kernel32 == null) {
            kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
        }
        return kernel32;
    }

    private Shell32 getShell32() {
        if (shell32 == null) {
            shell32 = (Shell32) Native.loadLibrary("shell32", Shell32.class);
        }
        return shell32;
    }

}

interface Kernel32 extends StdCallLibrary {
    int GetCurrentProcessId();

    WString GetCommandLineW();

    Pointer LocalFree(Pointer pointer);
}

interface Shell32 extends StdCallLibrary {
    Pointer CommandLineToArgvW(WString command_line, IntByReference argc);
}

除了众所周知的log4j之外,这段代码也依赖于

<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>4.3.0</version>
</dependency>

0
2018-01-29 16:58





Java在内部使用Unicode,因此在编译使用中文编码(如Big5或GB2312)的源代码文件时,需要为编译器指定编码,以便将其正确转换为Unicode。

javac -encoding big5 sourcefile.java 

要么

javac -encoding gb2312 sourcefile.java

参考: http://www.chinesecomputing.com/programming/java.html


-2
2017-10-05 11:38



这与问题完全无关。问题是关于在args中使用unicode符号调用应用程序。它不是关于编译其中包含unicode符号的源代码。 - Sergey Karpushin