gpt4 book ai didi

java - 如何从 Tomcat/Java 应用程序创建文件名带有 UTF-8 字符的系统文件?

转载 作者:行者123 更新时间:2023-11-30 09:43:02 24 4
gpt4 key购买 nike

我有一个创建 UTF-8 文件系统名称的 java 服务器应用程序。

不幸的是,当我查看文件名时,文件名的非 ascii 字符显示为“?”。如何让系统显示适当的 UTF-8 字符?

例如,我可以通过触摸从终端创建带有希腊字符的文件,并且所有 UTF-8 字符都正确显示。

系统规范

  • Linux CentOS 6.0 2.6.18.8-xenU #1 SMP Thu May 13 11:11:51 PDT 2010
    x86_64 x86_64 x86_64 GNU/Linux
  • Tomcat 6
  • Java 1.6

  • 配置
    JAVA_OPTS=-Dsun.jnu.encoding=UTF-8
    CATALINA_OPTS=-Dfile.encoding=UTF-8

    locale
    LANG=en_US.UTF-8
    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=

    我什至在启动时执行以下操作:
    System.setProperty("file.encoding", "UTF-8");
    System.setProperty("encoding", "UTF-8");
    System.setProperty("user.language", "en_US.UTF-8");
    System.setProperty("user.country", "en_US.UTF-8");
    System.setProperty("sun.jnu.encoding", "UTF8");

    我在哪里创建文件:
    fullPathName = new String(fullPathName.getBytes("UTF-8"));
    InputStream is = file.getInputStream();
    input = new BufferedInputStream(is, STREAM_BUFFER_SIZE);
    output = new BufferedOutputStream(new FileOutputStream(fullPathName),
    STREAM_BUFFER_SIZE);

    // Read file from memory and write it to disk.
    int r;
    byte[] buf = new byte[STREAM_BUFFER_SIZE];
    while ((r = input.read(buf)) != -1) {
    output.write(buf, 0, r);
    }

    output.close();
    output = null;
    input.close();
    input = null;

    最佳答案

    我对 Java 中的 String 的理解是它包含一串 Unicode 代码点,内部存储为 UTF-16。然而,这应该是许多 String 方法的实现细节。因此 getBytes 将返回一个字节数组,其中包含 fullPathName 中任何代码点的 UTF-8 编码,然后将这些字节传递给的 String 构造函数将其转换为 String 的内部编码,假设字节具有平台的编码.如果我们假设您已经设置好让 Java 认为 UTF-8 是平台编码,那么您最终会得到一个与原始字符串具有完全相同内容的字符串。

    所以问题是,你为什么要这么做?您是否做了类似将 UTF-8 代码单元放入字符串然后期望 getBytes("UTF-8") 返回包含这些代码单元的字节数组的操作?

    当您将字符串 fullPathName 传递给 FileOutputStream 时,您应该检查它包含的内容,因为最有可能的事情是您正在做的事情导致传递了错误的内容。

    另一种可能性是您的外壳实际上并未使用 UTF-8,因此当您使用希腊字符通过触摸创建文件时,您实际上只是使用了适合您的外壳设置方式的任何内容。因此,当 Java 使用 UTF-8 编码创建名称的文件时,您的 shell 会正确显示 UTF-8 文件名不是 shell 配置的任何编码。

    您可以通过 hexdump 管道显示文件名中使用的实际字节,然后手动确定文件名是 UTF-8 还是其他。

    哦,还有一件事。文件系统格式确实有影响,因此您可能需要列出它。尽管我假设您使用的是一些典型的不强制任何文件名编码的 linux 文件系统格式,但某些文件系统格式(如 NTFS 或 HFS+)以已知编码存储文件名,并且 API 必须处理它。例如(C 函数) fopen on 可能使用当前系统编码从给它的字节数组转码为 UTF-16,以便找出 UTF-16 代码单元以将文件存储在 NTFS 下。但是其他文件系统不强制执行任何编码,因此 fopen 只会获取您提供的字节数组并将其存储为文件名。这将导致行为差异实际上可能导致使用已知编码字符串的环境中的文件访问 API 出现问题。例如,如果您有一个将 UTF-16 字符串作为文件名的函数,并且您要打开的文件使用 ISO-8859-1 字节字符串命名,但系统的编码是 UTF-8,那么该文件访问 API 可能只是无法打开该文件。

    这整件事真的搞砸了。

    我正在添加一个示例。以下文件保存为 UTF-8 并命名为“HelloWorld.java”

    import java.io.BufferedOutputStream;
    import java.io.FileOutputStream;
    import java.io.FileNotFoundException;

    class HelloWorld {
    public static void main(String[] args) {
    String fullPathName = "ΘΙϗϕξ.tmp";
    for(int i=0;i<fullPathName.length();++i) {
    System.out.format("char: %x\n",
    (int)fullPathName.charAt(i));
    }

    try {
    BufferedOutputStream output =
    new BufferedOutputStream(
    new FileOutputStream(fullPathName));
    } catch(FileNotFoundException e) {
    System.out.println("caught exception");
    }
    }
    }

    使用 javac HelloWorld.java && java HelloWorld 构建和运行输出是:
    char: 152
    char: f2
    char: 152
    char: f4
    char: 153
    char: f3
    char: 153
    char: ef
    char: 152
    char: e6
    char: 2e
    char: 74
    char: 6d
    char: 70

    此输出表明字符串中有错误的字符。显然,即使我的系统设置了 en_US.UTF-8 语言环境,java 也不假定 UTF-8 源代码。使用 javac -encoding UTF-8 && java HelloWorld 构建和运行我得到以下正确的输出:
    char: 398
    char: 399
    char: 3d7
    char: 3d5
    char: 3be
    char: 2e
    char: 74
    char: 6d
    char: 70

    现在字符串包含正确的 UTF-16 代码单元并创建文件“ΘΙϗφξ.tmp”,该文件显示在目录中:
    0 [Hydrogen·bames·~/tmp]
    ⑆ ls
    HelloWorld.class
    HelloWorld.java
    ΘΙϗϕξ.tmp
    0 [Hydrogen·bames·~/tmp]
    ⑆ ls *.tmp | hexdump -C
    00000000 ce 98 ce 99 cf 97 cf 95 ce be 2e 74 6d 70 0a |...........tmp.|
    0000000f

    如您所见,FileOutputStream 正确转换为语言环境编码以创建文件,因为 ce 98U+0398 的正确 UTF-8 编码或“Θ”。

    目前尚不清楚日志文件中正确显示的文件名是否足以说明字符串的内容确实没问题。此外,了解您获得的文件名不仅仅是某些字符看起来像“?”也会很有帮助。存储的实际值是多少?您可以使用 hexdump 进行查找。

    关于java - 如何从 Tomcat/Java 应用程序创建文件名带有 UTF-8 字符的系统文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8440337/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com