java.nio.file.InvalidPathException:格式错误的输入或使用国家字符时输入包含不可映射的字符

Posted

技术标签:

【中文标题】java.nio.file.InvalidPathException:格式错误的输入或使用国家字符时输入包含不可映射的字符【英文标题】:java.nio.file.InvalidPathException: Malformed input or input contains unmappable characters when using national characters 【发布时间】:2017-01-04 06:23:54 【问题描述】:

我正在尝试创建一些具有“äöü”等国家符号的目录。不幸的是,每当尝试这样做时,我都会遇到此异常:

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

它似乎失败了 (new FileOutputStream(myOutputFile));当它尝试使用 File 对象初始化 FileOutputStream 时,该对象的路径是从从上述异常中的路径中检索到的字符串创建的,并在末尾添加了文件名。

所以现在创建了目录,但是在其中写入或创建任何内容仍然会导致上述异常,尽管其中的文件不包含国家符号。

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

【问题讨论】:

罪犯显然是 o-umlaut 字符。该目录是否已经存在?如果没有,您在执行mkdir /home/pi/myFolder/löwen 时会收到错误消息吗? @JimGarrison 是的,是 ö 字符导致了问题。不,路径还没有,因此如果它还没有,代码接下来会尝试创建它,但是当它还没有创建时它会失败。如果我通过 SSH 从 bash 执行 mkdir 命令,它会完美运行,这就是为什么我觉得这很奇怪。它可以与 Java/Tomcat 设置有关吗?但是 Tomcat 似乎有点设置为使用 UTF-8 进行文件编码,所以我不知道还有什么其他可能的点。 路径是硬编码在源代码中的某处还是用户输入或属性文件中?无论路径名的来源是什么,THAT 都在国家字符集中,并且由于某种原因未转换为 UTF-8,从而导致错误。 Unix 文件系统真的支持这样的文件名吗?可以从 shell 中创建吗? @LittleSanti 是的,我试过了。 【参考方案1】:

只需设置环境变量“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)

    Java 在此方法中将所有字符串本地转换为平台的本地编码:jdk/src/share/native/common/jni_util.c - JNU_GetStringPlatformChars()。系统属性 sun.jnu.encoding 用于确定平台的编码。

    sun.jnu.encoding 的值设置在 jdk/src/solaris/native/java/lang/java_props_md.c - GetJavaProperties() 使用 libc 的 setlocale() 方法。环境变量 LC_ALL 用于设置 sun.jnu.encoding 的值。使用 Java 的 -Dsun.jnu.encoding 选项在命令提示符处给出的值将被忽略。

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

【讨论】:

尝试将此作为评论发布或详细说明您的答案。【参考方案2】:

如果国家字符在您的源代码中是硬编码的,请将源文件转换为相同的编码。你可以使用 vim:

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

如果出现问题,您将收到一条消息(“不可映射的字符 (...)”)。

对我来说,问题与 1. 错误编码中的硬编码字符或 2. 在将路径传递给方法时以某种方式丢失编码有关。

【讨论】:

以上是关于java.nio.file.InvalidPathException:格式错误的输入或使用国家字符时输入包含不可映射的字符的主要内容,如果未能解决你的问题,请参考以下文章