11

我遇到了这个问题,并且已经有几天的搜索和解决方法尝试不成功。

我现在有一个由 jnlp/webstart 在 osx 和 windows 计算机上分发的内部 java swing 程序,除其他外,它从 WebDav 下载一些文件。

最近,在一台装有 OSX 10.8 和 Java 7 的测试机器上,带有重音字符的文件名和目录名开始被问号替换。

在 7 之前的 Java 版本的 OSX 上没有问题。

例子 :

XXXYYY_è_ABCD/

变成

XXXYY_?_ABCD/

在原始字符串上使用java.text.Normalizer (NFD, NFC, NFKD, NFKC),结果不同但仍然错误:

XXXYY_e?_ABCD/

或者

XXXYYY_e_ABCD/

我知道,根据 [andrew.brygin at oracle.com] 和 [mik3hall at gmail.com] 之间的通信,

是的,file.encoding 是根据运行 jvm 的语言环境设置的,如果你在 xxxx.UTF-8 语言环境中运行你的 java vm,file.encoding 应该是 UTF-8,设置为 MacRoman 会有问题。所以我相信 Oracle/OpenJDK7 的行为是正确的。也就是说,正如 Andrew 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 内部执行语言环境不变地打印

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 环境变量。

任何解决方案或解决方法?

PS:

如果我们直接从 shell 运行程序,它即使在 OSX 10+Java 7 上也能正确写入文件/目录。只有 JNLP+OSX+Java7 的组合才会出现问题

4

5 回答 5

5

我认为文件名的最大 ASCII 表示是可以接受的,它几乎适用于任何编码。

首先,您要专门使用 NFKD,以便以 ASCII 形式保留最大信息。例如,一旦过滤掉非 ascii 和非控制字符,就"2⁵"变成了,"25"而不是只是 "2""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

然后,您将始终通过此过滤器传递文件名以获取它们的文件系统名称。你唯一失去的是一些独特性,IE文件asdé.txt是一样的asde.txt,在这个系统中它们是无法区分的。

于 2012-12-10T12:53:23.103 回答
1

编辑:在尝试了更多 OS X 之后,我意识到我的答案完全错误,所以我正在重做。

如果您的 JVM 支持-Dfile.encoding=UTF-8JVM 命令行,那可能会解决问题。我相信这是一个标准属性,但我不确定。

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 编码的(即,只要它是 HFS Plus 文件系统,就可以在看到8F的地方使用字节值)。è但是当然,JVM 不能保证 HFS Plus 文件系统,并且 SMB 和 NFS 没有相同的编码保证,所以 JVM 不应该假设这种方案。

因此,您必须说服 JVM 使用 UTF-8 编码来解释文件和目录名称,以便将名称java.lang.String正确地读取为对象。

于 2012-12-07T21:48:53.120 回答
1

在黑暗中拍摄:文件编码不会影响文件名的创建方式,只会影响内容写入文件的方式 - 在这里查看这个人:http: //jonisalonen.com/2012/java-and-file-带有无效字符的名称/

这是来自 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 构造函数之前对其进行规范化。这有帮助吗?

于 2012-12-10T09:28:37.863 回答
0

我认为目前还没有真正解决这个问题的方法。

同时我得出的结论是,从程序内部打印的“C”环境变量来自 Java Web Start 沙箱,并且(显然,按照设计)你不能影响那些使用 jnlp 的环境变量。

接受的(公司接受的)解决方法/妥协是使用 bash 脚本中的 javaws 启动 jnlp。

显然,从浏览器或 finder 启动 jnlp 会创建一个未设置 LANG 的新沙盒环境(因此设置为等于 ASCII 的“C”)。从命令行启动 jnlp 而是从系统默认值打印正确的 LANG,从 shell 继承它。

这允许至少保留 jnlp 和依赖项的自动更新功能。

无论如何,我们向 Oracle 发送了错误报告,但我个人不希望它很快得到解决,如果有的话。

于 2013-03-26T14:52:49.267 回答
0

这是 old-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)
于 2014-02-24T13:11:24.540 回答