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

时间:2011-10-05 11:34:32

标签: java unicode command-line-arguments

我必须将日语命令行参数传递给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();
        }
    }

6 个答案:

答案 0 :(得分:13)

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

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

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

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

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

答案 1 :(得分:5)

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

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

另一种可能性是使用Java包装器和临时文件的组合将Unicode参数传递给Java应用程序。有关更多注释和包装器代码,请参阅我的博客Java, Xalan, Unicode command line arguments...

答案 2 :(得分:1)

https://en.wikipedia.org/wiki/Unicode_in_Microsoft_Windows#UTF-8

  

使用内部版本17035和Windows 10的2018年4月更新(名义版本17134),出现了“测试版:使用Unicode UTF-8进行全球语言支持”复选框,用于将区域设置代码页设置为UTF-8

这实际上对我有用。没有它,无论我将chcp设置为-Dsun.jnu.encoding还是将其提供为> java test "üůßβαa" üußßaa ,该参数总是乱码。

我有一个测试类,它只会打印传递给它的参数:

之前:

> chcp 65001
Active code page: 65001
> java test "üůßβαa"
uaa

有趣的是,使用sun.jnu.encoding = Cp1252,U + 03B2(β,β)将成为德国犀利(ß),而捷克语将成为普通u。

> java -Dsun.jnu.encoding=utf-8 test "üůßβαa"
?u??aa

嗯...

> java test "üůßβαa亀"
uaa?
Exception in thread "main" java.nio.file.InvalidPathException: Illegal char <?> at index 6: uaa?
        at sun.nio.fs.WindowsPathParser.normalize(Unknown Source)
        at sun.nio.fs.WindowsPathParser.parse(Unknown Source)
        at sun.nio.fs.WindowsPathParser.parse(Unknown Source)
        at sun.nio.fs.WindowsPath.parse(Unknown Source)
        at sun.nio.fs.WindowsFileSystem.getPath(Unknown Source)
        at java.nio.file.Paths.get(Unknown Source)
        at test.urify(test.java:33)
        at test.urify(test.java:43)
        at test.main(test.java:13)

这不是更好。而且当CJK字符(例如U + 4E80(亀))发挥作用时,情况变得更糟:

> calabash.bat Untitled3.xpl foo='rαaßβöů亊'
<doc xmlns:c="http://www.w3.org/ns/xproc-step" foo="rαaßβöů亊">Hello world!</doc>

class that I used不仅打印其参数,还将尝试将其转换为文件URI,然后崩溃。

将Windows语言环境设置为UTF-8 with the approach quoted above解决了此问题。

不幸的是,它无法解决传递给另一个Java程序XProc处理器XML Calabash的参数的编码问题。从命令行获取值并将其作为属性插入到文档中的示例管道产生了此mojibake:

-Dsun.jnu.encoding=UTF-8

在Java调用中添加<doc xmlns:c="http://www.w3.org/ns/xproc-step" foo="rαaßβöů亊">Hello world!</doc> 可以解决此问题:

-Dsun.jnu.encoding=UTF-8

为完整起见,在将Windows语言环境切换为UTF-8之前,根据代码页是1252还是65001,调用产生了-Dsun.jnu.encoding=UTF-8无法修复的mojibake的不同变体。

因此,切换Windows区域设置的beta功能似乎终于解决了此问题。某些应用可能需要额外的> java test t=r_ä亀 > type C:\Temp\abc.txt t=r_ä亀 ,原因尚未充分研究。

这不能解决您使用Windows 2000已有多年的问题。但是也许您同时已切换到Windows 10。

嗯,顺便说一句,我运行了您的程序,它可以与Windows UTF-8语言环境设置一起使用。

upstream backend {
   #least_conn;
   #ip_hash;
   server 172.29.24.1:5050 weight=3;
   server 172.29.24.1:6060 weight=5;
}

答案 3 :(得分:0)

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

以下是如何执行此操作http://www.java.com/en/download/help/locale.xml

答案 4 :(得分:0)

你可以使用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>

答案 5 :(得分:-2)

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

javac -encoding big5 sourcefile.java 

javac -encoding gb2312 sourcefile.java

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