java.nio.file.InvalidPathException: 当使用国际字符时出现格式错误的输入或输入包含无法映射的字符。

20

我正在尝试创建一些带有像"äöü"等国家符号的目录。不幸的是,每当尝试这样做时,我都会收到这个异常:

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是Raspberry Pi 2上的Raspbian:

$ 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 ...

有没有人有解决这个问题的方法?我需要能够在目录/文件名中使用那些国家符号...

编辑:

在我的Tomcat的setenv.sh开头添加了额外选项Dsun.jnu.encoding=UTF-8并重新启动后,某些内容已更改。

目前我的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时,似乎会在(new FileOutputStream(myOutputFile))上失败。

因此现在目录已经创建,但在其中编写或创建任何内容仍会导致上述异常,尽管其中的文件甚至不包含国际符号。

在没有国际符号的情况下创建路径和文件与在setenv.sh更改之前一样完美地工作,因此看起来问题仍然与路径中的国际符号有关...


罪魁祸首显然是带有o-umlaut字符。那个目录已经存在了吗?如果不存在,当您执行mkdir /home/pi/myFolder/löwen时是否会出现错误? - Jim Garrison
@JimGarrison 是的,正是那个ö字符导致了问题。不,路径还不存在,因此代码接下来会尝试创建它,但如果还没有创建,它就会失败。如果我通过SSH从bash运行mkdir命令,它可以完美地工作,这就是为什么我觉得这很奇怪。这可能与Java/Tomcat设置有关吗?但是Tomcat似乎已经设置为使用UTF-8进行文件编码,所以我不知道还有哪些其他可能的问题点。 - Arturas M
路径是在源代码中硬编码的,还是用户输入或者在属性文件中?无论路径名的来源是什么,它都是使用国家字符集,并且由于某种原因未被转换为UTF-8,导致了错误。 - Jim Garrison
Unix文件系统实际上支持这样的文件名吗?它可以从shell中创建吗? - Little Santi
@JimGarrison 不,路径由一些值组成,这些值从xml中读取,还有一部分(带有国家符号ö)从电子邮件中读取。嗯,目前我在setenv.sh的开头添加了额外选项Dsun.jnu.encoding=UTF-8,同时保留了另一个选项,似乎在重新启动后它不再抛出此异常,但它会抛出一个新的异常,这必须与同样的问题有关,我编辑了我的问题。 - Arturas M
1
@LittleSanti 可以的,我试过了。 - Arturas M
2个回答

4

只需设置环境变量"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()中使用libc的setlocale()方法设置。环境变量LC_ALL用于设置sun.jnu.encoding的值。使用-Dsun.jnu.encoding选项给Java提供的命令提示符中的值将被忽略。

(来自https://stackoverrun.com/cn/q/3020937)


尝试将此作为评论发布或详细说明您的答案。 - Deepak Kumar

1
如果您的源代码中硬编码了国际字符,请将源文件转换为相同的编码方式。您可以使用vim:
vim SourceClassWithHardcodedCharacters.java
:set fileencoding=utf-8<Enter>
:w<Enter>

如果出现问题,您将收到一条消息(“无法映射字符(...)”)。
对于我来说,问题要么与1.在不正确的编码中硬编码字符有关,要么与2.在传递路径到方法时不知何故丢失编码有关。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接