我遇到了这个问题,并且已经进行了几天不成功的搜索和解决方法尝试。
我现在有一个内部java swing程序,由jnlp / webstart在osx和windows计算机上发布,除其他外,还从WebDav下载一些文件。
最近,在使用OSX 10.8和Java 7的测试计算机上,带有重音字符的文件名和目录名开始被问号替换。
在7之前的Java版本的OSX上没问题。
示例:
XXXYYY_è_ABCD/
变为
XXXYYY _?_ ABCD /
在原始字符串上使用 java.text.Normalizer (NFD,NFC,NFKD,NFKC),结果不同但仍然错误:
XXXYYY_e?_ABCD /
或
XXXYYY_e_ABCD /
我知道,来自oracle.com的[andrew.brygin]和[mik3hall at gmail.com]之间的通信
是的,file.encoding是根据jvm正在运行的语言环境设置的 on,如果你在xxxx.UTF-8语言环境中运行java vm,那么 file.encoding应该是UTF-8,设置为MacRoman会有问题。 所以我相信Oracle / OpenJDK7的行为正确。那说,安德鲁 Thompson指出,如果所有以前的Apple JDK版本都使用MacRoman 作为英文/ UTF-8语言环境的file.encoding,有一个 这里的“兼容性”问题,可能值得一提 发布说明,为Oracle / OpenJDK MacOS用户提供了一个支持。
来自 Joni Salonen 博客(java-and-file-names-with-invalid-characters)我知道:
您可能知道Java使用“默认字符编码” 将二进制数据转换为字符串。使用他人阅读或书写文字 编码可以使用InputStreamReader或OutputStreamWriter。但 对于API深处的数据到文本转换,您别无选择 更改默认编码。
和
file.encoding怎么样?
file.encoding系统属性也可用于设置默认值 Java用于I / O的字符编码。不幸的是,似乎 对文件名如何解码为字符串没有影响。
从jnlp内部执行locale invariabily print
LANG=
LC_COLLATE="C"
LC_CTYPE="C"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=
使用解决方案的stackoverflow上最类似的问题是:
encoding-issues-on-java-7-file-names-in-os-x
但是解决方案是用脚本
包装java程序的执行#!/bin/bash
export LC_CTYPE="UTF-8" # Try other options if this doesn't work
exec java your.program.Here
但由于webstart,我不认为我可以使用此选项,而且我没有找到任何方法在程序中设置LC_CTYPE环境变量。
任何解决方案或解决方法?
P.S。 :
如果我们直接从shell运行程序,它甚至可以在OSX 10 + Java 7上正确写入文件/目录。 只有JNLP + OSX + Java7
的组合才会出现问题答案 0 :(得分:5)
我认为文件名的最大ASCII表示是可以接受的,几乎可以在任何编码中使用。
首先,您要特别使用NFKD,以便以ASCII格式保留最大信息。例如,"2⁵"
变为"25"
而不仅仅是"2"
一旦非ascii和非控制字符被过滤掉,"fi"
,"fi"
变为""
而不是String str = "XXXYYY_è_ABCD/";
str = Normalizer.normalize(str, Normalizer.Form.NFKD);
str = str.replaceAll( "[^\\x20-\\x7E]", "");
//The file name will be XXXYYY_e_ABCD no matter what system encoding
等。
asdé.txt
然后,您将始终通过此过滤器传递文件名以获取其文件系统名称。你只丢失了一些唯一性,I.E文件asde.txt
是一样的
如{{1}},在这个系统中,它们无法区分。
答案 1 :(得分:1)
编辑:在尝试使用OS X后,我意识到我的答案完全错了,所以我正在重做它。
如果您的JVM在JVM命令行上支持-Dfile.encoding=UTF-8
,则可能会解决此问题。我相信这是一个标准的财产,但我不确定。
HFS Plus与其他符合POSIX标准的文件系统一样,将文件名存储为字节。但与Linux的ext3文件系统不同,它强制文件名是有效的分解UTF-8。这可以在我的OS X系统上使用Python解释器看到,从空目录开始。
$ python
Python 2.7.1 (r271:86832, Jul 31 2011, 19:30:53)
>>> import os
>>> os.mkdir('\xc3\xa8')
>>> os.mkdir('e\xcc\x80')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 17] File exists: 'e\xcc\x80'
>>> os.mkdir('\x8f')
>>> os.listdir('.')
['%8F', 'e\xcc\x80']
>>> ^D
$ ls
%8F è
这证明文件系统上的目录名称不能进行Mac-Roman编码(即字节值为8F
,只要它是è
),只要它是HFS Plus文件系统即可。但是,当然,JVM不能确保HFS Plus文件系统,并且SMB和NFS没有相同的编码保证,因此JVM不应该采用这种方案。
因此,您必须说服JVM使用UTF-8编码来解释文件和目录名称,以便正确地将名称读作java.lang.String
个对象。
答案 2 :(得分:1)
在黑暗中拍摄:文件编码不会影响文件名的创建方式,只会影响内容如何写入文件 - 请在此处查看此人:http://jonisalonen.com/2012/java-and-file-names-with-invalid-characters/
以下是Apple的简短条目:http://developer.apple.com/library/mac/#qa/qa1173/_index.html
将此与http://docs.oracle.com/javase/tutorial/i18n/text/normalizerapi.html相比较,我认为您要使用
normalized_string = Normalizer.normalize(target_chars, Normalizer.Form.NFD);
在将文件名传递给File构造函数之前规范化文件名。这有帮助吗?
答案 3 :(得分:0)
我认为现在没有真正解决这个问题的方法。
与此同时,我得出结论,从程序内部打印的“C”环境变量来自Java Web Start沙箱,并且(显然,设计上)你不能影响使用jnlp的那些。
接受(由公司接受)解决方法/妥协是使用来自bash脚本的javaws启动jnlp。
显然,从浏览器或finder启动jnlp会创建一个未设置LANG的新沙箱环境(因此设置为等于ASCII的“C”)。 从命令行启动jnlp会从系统默认值中打印出正确的LANG,并从shell继承它。
这允许至少保留jnlp和依赖项的自动更新功能。
无论如何,我们向甲骨文发送了一个错误报告,但我个人并不希望它能在短期内得到解决,如果有的话。
答案 4 :(得分:0)
这是旧的skool java File api中的一个错误,也许只是在Mac上?无论如何,新的java.nio api工作得更好。我有几个文件包含无法使用java.io.File和相关类加载的unicode字符和内容。转换我的所有代码后使用java.nio.Path一切开始工作。我用java.nio.Files替换了org.apache.commons.io.FileUtils(它有同样的问题)......
...并确保使用适当的字符集读取和写入文件内容,例如:
Files.readAllLines(myPath, StandardCharsets.UTF_8)