【问题标题】:Get filename as UTF-8? (ä,ü,ö ... is always '?')将文件名获取为 UTF-8? (ä,ü,ö ... 总是 '?')
【发布时间】:2011-05-24 22:21:57
【问题描述】:

我必须读取一些文件的名称并将它们作为字符串放入列表中。没那么难我只是有一些像ä,ö,ü这样的字符的问题......它们总是作为'?在我的字符串中。

有什么问题?那么编码。好的,这应该很容易......这就是我的想法。所以我尝试使用如下功能:

new String(insert.getBytes("UTF-8") 要么 new String(insert.getBytes("ISO-8859-1"), "UTF-8") 因为大部分文件都是 ISO-8859-1

它没有帮助。这是我的代码:

...
File[] fileList = dir.listFiles();
String insert;
for(File f : fileList) {
...
insert=f.getName().substring(0,f.getName().length()-4);
                insert=insert.charAt(0)+insert.substring(1,insert.length()).toLowerCase().replaceFirst("([0-9]*(_s?(i)?(_dat)?)*$)", "").replaceFirst("_", " ");
...
System.out.println("test UTF8: " + new String(insert.getBytes("UTF-8"))); //not helping
System.out.println("test ISO , UTF8: " + new String(insert.getBytes("ISO-8859-1"), "UTF-8")); //not helping
...
names.add(insert);
}

最后有很多带有 '?' 的字符串我列表中的字符。 如何解决问题?如果不仅有 ISO-8859-1 文件,那么最好的方法是什么? (假设有很多未知的编码文件)

谢谢!

【问题讨论】:

  • 您是从磁盘还是从其他文件读取文件名?有一个首选的 OS 文件名编码,但如果你从一个编码未知的文件中获取它,你可能会遇到麻烦。
  • 如果您可以读取具有某种编码(正确的)的文件并将它们写回磁盘并且您没有收到奇怪的字符,那么您的编码处理就很好。在这种情况下,只是您的控制台没有显示字符。
  • @mdrg:嗯,有一个问题。我必须读取文件的名称,然后将它们放入数据库。还有很多“?” ,那不应该是……
  • @Lissy:不,你误会了。磁盘有正确的文件名,程序有正确的文件名,数据库甚至可能有正确的文件名,但是当您将文件名打印到屏幕上时,您的屏幕可能没有显示正确的字符。当然,如果ls 显示它并且yourprogram 显示问号,那是你的程序有问题。顺便说一句,你在什么操作系统和文件系统上运行它?
  • @Lissy:关键信息仍然缺失。当您在相关文件/目录上运行ls 时,它会报告什么?它打印特殊字符还是打印?也一样?

标签: java encoding utf-8


【解决方案1】:

鉴于问题下来回扩展 cmets,现在看起来这要么是字体问题,要么(可能更可能)是文件名编码问题。

我让 Lissy 运行以下命令,让我们找出问题所在。如果她确定文件名中包含“ä”,但是当她ls文件名时没有出现那个字符,那么这个命令会告诉我们这是字体问题还是编码问题。

touch filenäme
ls filen*me

如果这在ls 的输出中显示“filenäme”,那么我们知道问题出在将文件创建/复制到此系统上。如果创建文件的程序没有意识到文件系统编码是什么或者太愚蠢而无法做正确的事情,就会发生这种情况。 convmv 程序可能是解决此问题的最佳方法。

convmv -f ENCODING -t utf8 -r .

问题是什么是正确的编码。可能包括 UTF-16、cp850 或 iso8859-1。 convmv --list 将向您显示当前已知(对您的系统而言)编码的列表。由于上面列出的命令仅向您展示了它可能执行的操作,因此可以安全地使用不同的编码多次运行,直到找到一个对所有文件都有效的

如果这是字体问题,我们必须调查一下

【讨论】:

  • 嗨。 convmv 太棒了!我在我的桌面上尝试过它,它工作正常。问题是:在文件所在的 sftp 服务器上,没有 convmv,也永远不会有 convmv,我没有 root。
  • @Lissy:我唯一的想法是编写一个与 FTP 服务器交互的程序,并按照 convmv 使用的相同规则重命名它们。或者下载所有内容,删除,convmv,然后重新上传。
  • @Lissy:你可以自己编译it到你的home文件夹。
【解决方案2】:

字符串中出现意外的问号、spalts 等表示在从一个字符集转换为另一个字符集时某处无法识别特定字符。

在您的情况下,问题可能出现在几个地方:

  • 当您的 Java 程序从目录中读取文件名时(在 dir.listFiles() 调用中),可能会发生这种情况。

  • 当您将字符打印到控制台流时可能会发生这种情况。

在任何一种情况下,根本原因很可能是 Java 认为的语言环境设置与操作系统和/或命令 shell 正在使用的设置不匹配。

作为一个实验,尝试从命令行列出包含有问题的文件名的目录。你在那儿看到问号或其他问题吗?

要执行的第二个实验是修改您的 Java 程序以将一个问题字符串转储为代表每个字符的字符代码的数字序列。您是否看到 ASCII / Unicode '?' 的字符代码。

【讨论】:

  • 嗯,是的,但我相信? 是一个明确的信号,表明某个地方的某个人在他们应该有的时候忽略了引发异常。永远不要让某事产生?
  • @tchrist ? 在定义明确的情况下用作“替换字符”,这是在许多应用程序(shell、Web 浏览器等)中广泛使用的行为。如果需要,在 Java 中,可以更改此行为以引发异常,但这不是默认设置。
  • @erickson: ? 特别推荐反对;这是 Java 的缺陷之一。如果需要,应该使用 Unicode 替换字符,但最好引发异常,因为它表明您做错了,而不是掩盖错误。
  • Java 将用 U+FFFD 的编码替换不可编码的字符 如果目标编码可以编码 U+FFFD。但是,大多数编码不能。 UTF-8、UTF-16 等是少数例外。如果编码无法编码 U+FFFD,则 '?'是通常的后备,因为(几乎?)每种编码都可以支持它。
  • @tchrist - "...但最好提出异常..."。我不同意。每次出现不可映射的字符时抛出异常会导致大量文档等无法从 Java 访问。想象一下,您的 Web 浏览器拒绝显示包含声明的文档编码不支持的长破折号或时髦引号字符的页面。坏主意!
【解决方案3】:

文件名内容的编码与文件名本身的编码无关。

你应该从System.out.println(insert)得到正确的结果

如果您不这样做,则意味着 shell 具有与系统的默认字符编码不同的字符编码(这种情况很少发生;它通常是在 shell 中切换编码的显式命令的结果)。

如果在 shell 中列出目录时文件名正确显示,我希望它们可以正确显示而无需在 Java 程序中指定编码。


如果 shell 无法显示字符(它正在用替换字符 0xFFFD (�) 代替这些不可打印的字符),那么您无法从 Java 应用程序中进行任何更改。需要更改终端字符编码、安装正确的字体等;这是操作系统问题,不是 Java 问题。

同时,即使您的终端无法显示正确的结果,Java 程序也应该在没有您干预的情况下正确处理字符编码。

File API 背后的库正在为您的系统找出正确的字符编码,并对字符进行必要的解码。同样,数据库驱动程序应与数据库协商以确定正确的编码,并代表您的应用程序执行任何必要的字节编码。

【讨论】:

  • 如果我尝试 System.out.println(insert) 它会出现“F?st”之类的东西
  • 如果我在终端中尝试使用 ls -l 然后 "F�st"
  • @Lissy:您是否明确设置了System.out 的编码?如果不是,那就是问题所在。例如,您应该有一个stdout = new PrintStream(System.out, true, "UTF-8"); 或类似的某个地方。您也可以经常使用java -Dfile.encoding=UTF-8 运行,但这在技术上是只读的。
  • @tchrist: 即使我使用 new PrintStream(System.out, true, "UTF-8");有一个�
【解决方案4】:

在你写的评论中:

@mdrg:嗯,有一个问题。我必须读取文件的名称,然后将它们放入数据库。还有很多“?” ,那不应该是...... – Lissy 27 分钟前

我的猜测是,您插入文件名的列指定 US-ASCII 作为编码,并用替换字符替换该范围之外的字符,在您的情况下是问号。

因此,您必须找出存储文件名的数据库表中列的编码。各种产品具有用于检索该信息的各种语法。

【讨论】:

    【解决方案5】:

    在 Java 1.6 中,您可以使用 System.console() 代替 System.out.println() 向控制台显示重音字符。

    public class Test {
      public static void main(String args[]){
       String s = "caractères français :  à é \u00e9"; // Unicode for "é"
       System.console().writer().println(s);
      }
    }
    

    输出是

    C:\temp>java Test
    caractères français :  à é é
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-10
      • 2020-11-10
      • 2013-06-25
      • 1970-01-01
      • 1970-01-01
      • 2012-06-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多