java.nio.file.InvalidPathException: искаженный ввод или ввод содержит несопоставляемые символы при использовании национальных символов

Я пытаюсь создать несколько каталогов с национальными символами, такими как «äöü» и т. д. К сожалению, я получаю это исключение всякий раз, когда это делается:

java.nio.file.InvalidPathException: Malformed input or input contains unmappable characters: /home/pi/myFolder/löwen
        at sun.nio.fs.UnixPath.encode(UnixPath.java:147)
        at sun.nio.fs.UnixPath.<init>(UnixPath.java:71)
        at sun.nio.fs.UnixFileSystem.getPath(UnixFileSystem.java:281)
        at java.nio.file.Paths.get(Paths.java:84)
        at org.someone.something.file.PathManager.createPathIfNecessary(PathManager.java:161)
...
        at java.lang.Thread.run(Thread.java:744)

Мой код, где это происходит, выглядит так:

public static void createPathIfNecessary(String directoryPath) throws IOException {
        Path path = Paths.get(directoryPath);
        // if directory exists?
        if (!Files.exists(path)) {
            Files.createDirectories(path);
        } else if (!Files.isDirectory(path)) {
            throw new IOException("The path " + path + " is not a directory as expected!");
        }
    }

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

 $ locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

У меня нет этой проблемы в Windows 7, она отлично создает каталоги, поэтому мне интересно, нужно ли мне улучшить код Java, чтобы лучше справляться с этой ситуацией, или что-то изменить в моем Linux.

Linux, на котором я его запускаю, — это Raspbian на Raspberry Pi 2:

$ cat /etc/*-release

    PRETTY_NAME="Raspbian GNU/Linux 7 (wheezy)"
    NAME="Raspbian GNU/Linux"
    VERSION_ID="7"
    VERSION="7 (wheezy)"
    ID=raspbian
    ID_LIKE=debian
    ANSI_COLOR="1;31"
    HOME_URL="http://www.raspbian.org/"
    SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
    BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

Я запускаю свое приложение на сервере Tomcat 7 (я полагаю, версия Java 1.8), мой setenv.sh начинается с: export JAVA_OPTS="-Dfile.encoding=UTF-8 ...

У кого-нибудь есть решение этой проблемы? Мне нужно иметь возможность использовать эти национальные символы в именах каталогов/файлов...

ИЗМЕНИТЬ:

После добавления дополнительной опции Dsun.jnu.encoding=UTF-8 при запуске моего setenv.sh для Tomcat и перезапуска что-то изменилось.

В настоящее время мой запуск setenv.sh выглядит так

export JAVA_OPTS="-Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8 

похоже, что это исключение исчезло, и создается папка с национальными символами, однако проблема, похоже, не решена полностью, всякий раз, когда я пытаюсь создать/записать файлы в этом каталоге, теперь я получаю:

java.io.FileNotFoundException: /home/pi/myFolder/löwen/Lowen.tmp (No such file or directory)
        at java.io.FileOutputStream.open(Native Method)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:206)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:156)
        at org.someone.something.MyFileWriter.downloadFiles(MyFileWriter.java:364)
        ...
        at java.lang.Thread.run(Thread.java:744)

Код, где это происходит, выглядит так:

// output here
File myOutputFile = new File(filePath);
FileOutputStream out = (new FileOutputStream(myOutputFile));
out.write(bytes);
out.close();

Кажется, это не удается (новый FileOutputStream (myOutputFile)); когда он пытается инициализировать FileOutputStream с помощью объекта File, у которого есть путь, созданный из строки, которая была получена из пути в исключении выше, и добавленное имя файла в конце.

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

Создание путей и файлов в них, когда они не имеют национальных символов, работает так же отлично, как и до изменения setenv.sh, поэтому похоже, что проблема все еще связана с национальными символами внутри пути...


person Arturas M    schedule 27.08.2016    source источник
comment
Преступник явно является персонажем о-умлаут. Этот каталог уже существует? Если нет, вы получаете сообщение об ошибке при выполнении mkdir /home/pi/myFolder/löwen?   -  person Jim Garrison    schedule 28.08.2016
comment
@JimGarrison Да, проблема связана с символом ö. Нет, пути еще нет, поэтому следующий код пытается создать его, если он еще не создан, но терпит неудачу, когда он еще не создан. Если я выполняю команду mkdir из bash через SSH, она работает отлично, вот почему я нахожу это таким странным. Может ли это быть связано с настройкой Java/Tomcat? Но Tomcat, похоже, несколько настроен для кодирования файлов с помощью UTF-8, поэтому я не знаю, какие еще есть возможные точки.   -  person Arturas M    schedule 28.08.2016
comment
Является ли путь жестко закодированным где-то в исходном коде или он введен пользователем или в файле свойств? Каким бы ни был источник имени пути, THAT находится в национальном наборе символов и по какой-то причине не преобразуется в UTF-8, что приводит к ошибке.   -  person Jim Garrison    schedule 28.08.2016
comment
Поддерживает ли файловая система Unix такое имя файла? Можно ли его создать из оболочки?   -  person Little Santi    schedule 28.08.2016
comment
@JimGarrison Нет, путь состоит из некоторых значений, которые считываются из xml, и части (с национальным символом ö), считываемой из электронной почты. Хм, в настоящее время я добавил дополнительную опцию в начале setenv.sh Dsun.jnu.encoding=UTF-8, оставив другую, и кажется, что после перезапуска она больше не выдает это исключение, а выдает новое и это должно быть связано с той же проблемой, я отредактировал свой вопрос   -  person Arturas M    schedule 28.08.2016
comment
@LittleSanti Да, я пытался.   -  person Arturas M    schedule 28.08.2016


Ответы (2)


просто установите переменные среды LANG=en_US.UTF-8 или какой-либо другой xxx.UTF-8.(https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html)

JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
                                            jobject file)
{
    jboolean rv = JNI_FALSE;
 
    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
        if (mkdir(path, 0777) == 0) {
            rv = JNI_TRUE;
        }
    } END_PLATFORM_STRING(env, path);
    return rv;
}
#define WITH_PLATFORM_STRING(env, strexp, var)                                
    if (1) {                                                                  
        const char *var;                                                      
        jstring _##var##str = (strexp);                                       
        if (_##var##str == NULL) {                                            
            JNU_ThrowNullPointerException((env), NULL);                       
            goto _##var##end;                                                
        }                                                                     
        var = JNU_GetStringPlatformChars((env), _##var##str, NULL);           
        if (var == NULL) goto _##var##end;
 
#define WITH_FIELD_PLATFORM_STRING(env, object, id, var)                      
    WITH_PLATFORM_STRING(env,                                                 
                         ((object == NULL)                                    
                          ? NULL                                              
                          : (*(env))->GetObjectField((env), (object), (id))), 
                         var)
  1. Java изначально переводит все строки в локальную кодировку платформы следующим методом: jdk/src/share/native/common/jni_util.c - JNU_GetStringPlatformChars() . Системное свойство sun.jnu.encoding используется для определения кодировки платформы.

  2. Значение sun.jnu.encoding устанавливается в jdk/src/solaris/native/java/lang/java_props_md.c — GetJavaProperties() с использованием метода setlocale() библиотеки libc. Переменная среды LC_ALL используется для установки значения sun.jnu.encoding. Значение, указанное в командной строке с использованием параметра -Dsun.jnu.encoding для Java, игнорируется.

(из https://stackoverrun.com/cn/q/3020937)

person Hanson    schedule 06.08.2020
comment
Попробуйте опубликовать это как комментарий или уточнить свой ответ. - person Deepak Kumar; 06.08.2020

Если национальные символы жестко закодированы в исходном коде, преобразуйте исходный файл в ту же кодировку. Вы можете использовать вим:

vim SourceClassWithHardcodedCharacters.java
:set fileencoding=utf-8<Enter>
:w<Enter>

Если есть проблема, вы получите сообщение ("неотображаемый символ (...)").

Для меня проблема связана либо с 1. жестким кодированием символов в неправильной кодировке, либо 2. с потерей кодировки каким-то образом при передаче пути к методу.

person Krzysztof Kaszkowiak    schedule 27.08.2016