Передача аргумента unicode командной строки в код Java

Мне нужно передать аргумент командной строки, который является основным для японского языка Java. Если я набираю символы 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();
        }
    }

Ответ 1

К сожалению, вы не можете надежно использовать символы, отличные от ASCII, с приложениями из командной строки, которые используют среду stdlib для Windows C, например Java (и почти все языки сценариев, не относящиеся к Windows).

Это потому, что они читают свой ввод и вывод, используя стандартную кодовую страницу по умолчанию, которая никогда не является UTF, в отличие от любой другой современной ОС, которая использует UTF-8.

В то время как вы можете изменить кодовую страницу терминала на что-то еще с помощью команды chcp, поддержка кодировки UTF-8 под chcp 65001 нарушена несколькими способами, которые, скорее всего, будут отключать приложения.

Если вам нужен только японский, вы можете перейти на кодовую страницу 932 (аналогично Shift-JIS), установив в Японию свой язык ( "язык для приложений, отличных от Юникода" в региональных настройках). Это все равно не будет выполнено для символов, которые не находятся на этой кодовой странице.

Если вам нужно получить символы, отличные от ASCII, через командную строку надежно в Windows, вам нужно вызвать функцию API Win32 GetCommandLineW, чтобы избежать слоя с кодировкой на системной кодовой странице. Возможно, вы захотите сделать это с помощью JNA.

Ответ 2

К сожалению, стандартная Java-пусковая установка имеет известную и долговечную ошибку при обработке аргументов командной строки Unicode в Windows. Возможно, на некоторых других платформах тоже. Для обновления Java 7 1 он все еще был на месте.

Если вы хорошо себя чувствуете при программировании на C/С++, вы можете попробовать написать свою собственную пусковую установку. Некоторая специализированная пусковая установка может быть не большой проблемой... Посмотрите исходный пример на странице JNI Invocation API.

Другая возможность - использовать комбинацию Java-оболочки и временный файл для передачи параметров Unicode в Java-приложение. Смотрите мой блог аргументы командной строки Java, Xalan, Unicode... для получения дополнительных комментариев и кода оболочки.

Ответ 3

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

В инсайдерской сборке 17035 и обновлении за апрель 2018 года (номинальная сборка 17134) для Windows 10 появился флажок "Бета-версия: используйте Unicode UTF-8 для поддержки всемирной языковой поддержки" для установки кодовой страницы локали в UTF-8.

Это на самом деле работает для меня. Без этого, независимо от того, что я установил для chcp или что я поставил как -Dsun.jnu.encoding, аргумент всегда искажался.

У меня был тестовый класс, который просто печатал бы аргумент, который ему передан:

До:

> java test "üůßβαa"
üußßaa

Интересно, что с sun.jnu.encoding = Cp1252, U + 03B2 (бета, β) станет немецким резким s (ß), а чешский ů станет простым u.

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

Хм...

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

Это не лучше. И становится хуже, когда в игру вступают символы CJK, например U + 4E80 (亀):

> 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)

Класс, который я использовал, не только печатает свой аргумент, он также пытается преобразовать его в файл: URI, и он потерпел крах.

Установка языкового стандарта Windows на UTF-8 с помощью вышеприведенного подхода решает эту проблему.

К сожалению, это не решило проблемы кодирования с аргументами, передаваемыми другой Java-программе, процессору XProc XML Calabash. Пример конвейера, который берет значение из командной строки и вставляет его в качестве атрибута в документ, получивший этот mojibake:

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

Добавление -Dsun.jnu.encoding=UTF-8 к вызову Java исправило это:

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

Для полноты, перед переключением языкового стандарта Windows на UTF-8, в зависимости от того, была ли кодовая страница 1252 или 65001, вызов привел к различным вариациям mojibake, которые -Dsun.jnu.encoding=UTF-8 мог исправить.

Таким образом, бета-функция для переключения локали Windows наконец-то, кажется, решает эту проблему. Некоторым приложениям может потребоваться дополнительный -Dsun.jnu.encoding=UTF-8 по причинам, которые не были тщательно исследованы.

Это не решит вашу давнюю проблему с Windows 2000. Но, возможно, вы переключились на Windows 10 в то же время.

Ах, кстати, я запустил вашу программу, и она работает с настройкой локали Windows UTF-8.

> java test t=r_ä亀
> type C:\Temp\abc.txt
t=r_ä亀

Ответ 4

Проблема связана с вашей системой. Измените свой язык на японский, и он будет работать.

Вот как это сделать http://www.java.com/en/download/help/locale.xml

Ответ 5

Вы можете использовать JNA, чтобы получить это, здесь copy-paste из моего кода:

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>

Ответ 6

Java работает внутри с Unicode, поэтому при компиляции файлов исходного кода, которые использовали китайскую кодировку, такую ​​как Big5 или GB2312, вам нужно указать кодировку для компилятора, чтобы правильно преобразовать ее в Unicode.

javac -encoding big5 sourcefile.java 

или

javac -encoding gb2312 sourcefile.java

Ссылка: http://www.chinesecomputing.com/programming/java.html