Java项目属性中的Netbeans中文字符运行参数

yws3nbqq  于 2022-11-10  发布在  Java
关注(0)|答案(1)|浏览(200)

我正在写一个Netbeans java程序来处理参数中的中文字符。我可以在编码中使用unicode代码和中文字符,它们可以正确编译并显示在控制台上。但是,当我通过项目属性运行参数传递中文字符时,他们都变成了?????。我已经设置我的项目编码为UTF-8和VM选项-Dfile.encoding=UTF-8。这是我的代码,请帮助。

public static void main(String[] args) {
    String test = "\u5973\u58eb";
    System.out.println(test);      //works
    String test2 = "女士2";
    System.out.println(test2);     //works
    System.out.println(args[0]);   //copy&paste test2 to argument, not works, showing ??
}

注意:我使用jdk 1.8.0_202,Ant v1.10.4,Windows 10,Netbeans 10,用于Netbeans输出和终端的字体是monospaced14,项目是使用Netbeans“java应用程序”创建的。在Netbeans内或Windows命令提示符chcp 65001(均带有-Dfile.encoding=UTF-8)下运行时出现问题。
注意:在skomisa的建议下,我进一步测试了Windows“使用unicode utf8支持全球语言”。我也放弃了Netbeans,但使用jar在Windows cmd上运行chcp 65001。

D:\.......>java -cp dist/myjar.jar -Dfile.encoding=UTF8 java.mypackage.TestUTF8 女士
女士
女士2
女士

D:\.......>java -cp dist/myjar.jar java.mypackage.TestUTF8 女士
??
??2
女士

因此,使用-Dfile.encoding=UTF8(第一次运行),常量字符串在编码中工作,而参数不工作。没有-Dfile.encoding(第二次运行),代码中的常量不工作,而参数工作。但我需要两者。我有中文常量字符串在我的程序以及程序参数。谁能告诉我可以做什么吗?

gv8xihay

gv8xihay1#

基于对SO中类似问题的回答,似乎将Unicode参数传递给Java应用程序从来没有正常工作过。没有简单的解决方案,但可以使用JNA(Java Native Access,Java本地访问)来解决这个问题。
JNA允许您从Java调用Windows API方法,而无需使用native代码。因此,在Java应用程序中,您可以直接调用Win API方法(如GetCommandLineW()CommandLineToArgvW()),以访问有关用于调用程序的命令行的详细信息,包括传递的任何参数。这两种方法都支持Unicode。
实现这一点的代码并不简单,但也不过分复杂。
要编译代码,您需要几个jar:jna.jarjna-platform.jar。您可以从the JNA 5.10.0 downloadfrom Mavendist目录中取得这些档案。
此方法既适用于NetBeans,也适用于Windows 10上的命令行,但存在一些明显的差异:

  • 您必须从命令行调用chcp 65001,并在java.exe调用中指定**-Dfile.encoding=UTF-8**。
  • 在提取CommandLineToArgvW()返回的参数时,您可能会发现NetBeans中返回的参数与命令行中返回的参数之间存在差异。但这并不是一个真正的问题,因为您唯一感兴趣的参数是位于末尾的参数,它们位于包含jar文件名的参数之后。

下面是代码:

package chinesearg;

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;
import java.util.ArrayList;
import java.util.List;

// Proof of concept application which uses JNA to correctly process command
// line arguments containing Chinese characters using JNA. 
//
// Credit to Sergey Karpushin for the approach used in this this code.
// See this SO answer: https://stackoverflow.com/a/41923480/2985643
public class ChineseArg {

    private final Kernel32 kernel32 = Native.load("kernel32", Kernel32.class);
    private final Shell32 shell32 = Native.load("shell32", Shell32.class);

    public static void main(String[] args) {

        String test = "\u5973\u58eb";
        System.out.println(test);      //works
        String test2 = "女士2";
        System.out.println(test2);     //works
        System.out.println("args.length=" + args.length);
        for (int i=0; i< args.length; i++) {
            System.out.println("args[" + i + "] = "+args[i]);
        }
        String[] params = new ChineseArg().getCommandLineArguments();
        if (params == null) {
            System.out.println("getCommandLineArguments() returned null.");
        } else {
            int count = params.length;
            System.out.println("Number of params=" + count);
            for (int i = 0; i < count; i++) {
                System.out.println("params[" + i + "]=" + params[i]);
            }
        }
    }

    private String[] getCommandLineArguments() {

        System.out.println("Active code page is " + Kernel32.INSTANCE.GetConsoleCP());
        String[] ret = getFullCommandLine();
        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]);
        }
        return ret;
    }

    private String[] getFullCommandLine() {

        IntByReference argc = new IntByReference();
        Pointer argv_ptr = shell32.CommandLineToArgvW(kernel32.GetCommandLineW(), argc);
        String[] argv = argv_ptr.getWideStringArray(0, argc.getValue());
        kernel32.LocalFree(argv_ptr);
        return argv;
    }
}

interface Kernel32 extends StdCallLibrary {
    static Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class, com.sun.jna.win32.W32APIOptions.DEFAULT_OPTIONS);
    WString GetCommandLineW();
    int GetConsoleCP();
    Pointer LocalFree(Pointer pointer);
}

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

This is sample output when run from the Command Prompt, showing that the first argument ("女士2") is captured correctly:

C:\Users\johndoe>chcp 65001
Active code page: 65001

C:\Users\johndoe>java -Dfile.encoding=UTF-8 -jar "D:\NB126\ChineseArg\dist\ChineseArg.jar" "女士2"  "\u5973\u58eb"
女士
女士2
args.length=2
args[0] = ??2
args[1] = \u5973\u58eb
Active code page is 65001
Number of params=2
params[0]=女士2
params[1]=\u5973\u58eb

C:\Users\johndoe>

备注:

  • 这段代码是为了解决Windows环境中的一个限制。我不知道如果这段代码在macOS或Linux上运行会发生什么。
  • 虽然这与你的问题的精神相悖,但还有一种替代方法:将参数作为转义的Unicode传递给应用程序。使用Apache的StringEscapeUtils.unescapeJava()对数据进行反转义是很简单的。如果这是可行的,那么就根本不需要JNA。

相关问题