V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
string2020
V2EX  ›  程序员

有谁会,使用 Java 打开一个 文件名是 GBK 编码的文件?(在系统编码为 UTF-8 的 Linux 下,不能使用 convmv 进行文件名转码)

  •  
  •   string2020 · 2023-03-09 13:56:11 +08:00 · 2496 次点击
    这是一个创建于 385 天前的主题,其中的信息可能已经有所发展或是发生改变。
    31 条回复    2023-03-10 09:36:04 +08:00
    string2020
        1
    string2020  
    OP
       2023-03-09 13:58:45 +08:00
    很无语,实在不行只能使用 convmv 了
    string2020
        2
    string2020  
    OP
       2023-03-09 14:01:50 +08:00
    chatgpt 也是推荐 convmv 。真没其他办法了吗?
    AoEiuV020CN
        3
    AoEiuV020CN  
       2023-03-09 15:21:33 +08:00
    不太理解,这个第一反应难道不是 FileReader ?还是说 fileReader 遇到了什么问题?
    java.io.FileReader#FileReader(java.lang.String, java.nio.charset.Charset)
    AoEiuV020CN
        4
    AoEiuV020CN  
       2023-03-09 15:23:25 +08:00
    哦,是说文件名,不是文件内容啊,
    那从 parent 入手,listFiles 一个一个判断文件名是否是你要打开的那个试试,
    AoEiuV020CN
        5
    AoEiuV020CN  
       2023-03-09 15:27:48 +08:00
    我问了下 chatGPT ,他的意思是直接 new String 时指定编码没有问题,
    我没试过,但你可以试试,把字符串 getBytes 指定编码 gbk ,再 new String 指定系统编码 utf8 ,再传给 File ,
    string2020
        6
    string2020  
    OP
       2023-03-09 16:25:16 +08:00
    这是个蛋疼的事。
    你指定 utf-8 文件名去访问。文件系统里确实没有 和你指定的文件名相同的文件。因为他实际是 GBK 编码的。
    如果你指定 GBK 编码的文件名去访问,实际是个乱码字符串,那在文件系统里更不存在这个文件名为乱码的文件了。
    实际上就算在 linux 控制台,你也很难根据名字访问一个 gbk 文件名的文件。
    string2020
        7
    string2020  
    OP
       2023-03-09 16:26:04 +08:00
    @AoEiuV020CN listFiles 拿到的文件名就是乱码了。直接使用拿到的 file.exists()都不存在了
    AoEiuV020CN
        8
    AoEiuV020CN  
       2023-03-09 18:30:02 +08:00
    试了完全不行,
    不管是 File, Path, URI ,不管怎么指定的文件名,最终都会被解析成当前 jvm 编码,当前编码无法表示的字节就没有任何办法,
    像 0x01 这种完全无法打印的 java 还能正常处理,
    像 0xCE 这种,无论如何都会被识别成一个 utf8 字符,然后转成三个字节,变不回 0xCE,
    zhilincom
        9
    zhilincom  
       2023-03-09 18:52:06 +08:00
    查到这篇文章,不知道有用没? https://developer.aliyun.com/article/253507
    文件名编码和系统配置相关,JVM 好像不支持自定义文件名编码吧?要么直接 JNA 调用系统函数操作文件?
    billlee
        10
    billlee  
       2023-03-09 19:00:21 +08:00
    Java 改 JVM 编码到 ISO_8859_1, 然后 new String("汉字".getBytes("GBK"). StandardCharsets.ISO_8859_1) 应该可以。不能改 JVM 编码应该就没办法了。
    jorneyr
        11
    jorneyr  
       2023-03-09 20:18:03 +08:00
    InputStreamReader(InputStream in, Charset cs): 创建 reader 的时候指定编码。
    felixlong
        12
    felixlong  
       2023-03-09 22:02:20 +08:00
    @string2020 Linux 的文件系统没有 GBK 的概念。你只把他当成 char* 就行。你的问题在于怎么用 Java 的 String 表示这些 char 而已。 直接用 unicode:String filename = "\u0001\u00CE"。
    kenvix
        13
    kenvix  
       2023-03-09 22:13:48 +08:00
    看看底层 API 有没有办法拿到 byte[]型的文件名吧,先让 JVM 别急着 byte[]转 String
    AoEiuV020CN
        14
    AoEiuV020CN  
       2023-03-09 22:14:48 +08:00 via Android
    @felixlong 不行的,你可以试一下,
    0x01 这种可以处理,0xCE 这种会被转来转去最后得到错误的文件名,
    AoEiuV020CN
        15
    AoEiuV020CN  
       2023-03-09 22:16:43 +08:00 via Android
    @kenvix 关键就是没有办法,不管哪个 api ,暴露的都是 String ,
    leonshaw
        16
    leonshaw  
       2023-03-09 22:23:27 +08:00
    JNI...
    SoloCompany
        17
    SoloCompany  
       2023-03-09 22:55:50 +08:00
    Files.newInputStream(Paths.get(new URI("file:///path/to/" + URLEncoder.encode("中文", "GB18030"))))
    AoEiuV020CN
        18
    AoEiuV020CN  
       2023-03-09 23:00:49 +08:00
    说到 jni 的话就想到 jna , 用 jna 就完全没问题了,

    https://gist.github.com/AoEiuV020/668ba0ab3ae96db95ecfd07fe04dec21
    xiangyuecn
        20
    xiangyuecn  
       2023-03-09 23:07:34 +08:00
    这个跟编码没有关系吧,你 java 先把文件夹里面的文件名全部打印出来,再复制粘贴不就好了

    难道说还是有什么反人类的 bug
    leonshaw
        21
    leonshaw  
       2023-03-09 23:23:39 +08:00
    @AoEiuV020CN 打开文件拿到 fd 以后应该就可以直接用 java.io 再打开 /proc/self/fd/x 了
    tramm
        22
    tramm  
       2023-03-10 08:32:47 +08:00
    有啥问题?
    先把 utf8 的文件名转成 gbk 的 string, 然后再用 gbk 的 string 去访问不就行了嘛.

    我用下面的试了下, 可以访问的啊, 还是说我理解错了你的意思?(FileUtil 是 HuTool 中的)

    @Test
    public void charsetTest(){
    String content = "草泥马";
    byte[] gbks = content.getBytes(Charset.forName("GBK"));

    // gbk 在 UTF8 下的字符串表示
    String gbkInUtf8 = StrUtil.str(gbks, StandardCharsets.UTF_8);
    log.info(gbkInUtf8); // ������
    FileUtil.writeUtf8String("fuck", "F:\\Projects\\Learn\\JavaProjects\\aio-learn\\Logs\\"+gbkInUtf8+".txt");

    String s1 = FileUtil.readUtf8String("F:\\Projects\\Learn\\JavaProjects\\aio-learn\\Logs\\" + gbkInUtf8 + ".txt");
    log.info(s1);

    List<String> fileNames = FileUtil.listFileNames("F:\\Projects\\Learn\\JavaProjects\\aio-learn\\Logs");
    for (String fileName : fileNames) {
    String s = FileUtil.readString("F:\\Projects\\Learn\\JavaProjects\\aio-learn\\Logs\\" + fileName, StandardCharsets.UTF_8);
    log.info("{} - {}",fileName,s);
    }
    }
    tramm
        23
    tramm  
       2023-03-10 08:34:04 +08:00
    @tramm
    打印:
    2023-03-10 08:27:31.489 [main] [INFO ] com.wdnj.xxb.jtt.CommonTest - ������
    2023-03-10 08:27:31.492 [main] [INFO ] com.wdnj.xxb.jtt.CommonTest - fuck
    2023-03-10 08:27:31.497 [main] [INFO ] com.wdnj.xxb.jtt.CommonTest - ������.txt - fuck
    AoEiuV020CN
        24
    AoEiuV020CN  
       2023-03-10 09:01:21 +08:00 via Android
    @tramm 按道理是这样的,但是你离开 java 再看看这个文件名的十六进制,压根不是 gbk 编码的汉字,
    string2020
        25
    string2020  
    OP
       2023-03-10 09:25:11 +08:00
    @billlee 改了编码,日志什么的其他操作是不是也受到到影响
    string2020
        26
    string2020  
    OP
       2023-03-10 09:25:38 +08:00
    @jorneyr 是文件名的编码不对,文件 exists 都不存在。
    string2020
        27
    string2020  
    OP
       2023-03-10 09:26:13 +08:00
    @felixlong 已经用 convmv 处理了,有空我试试
    string2020
        28
    string2020  
    OP
       2023-03-10 09:26:53 +08:00
    @AoEiuV020CN 确实蛋疼
    string2020
        29
    string2020  
    OP
       2023-03-10 09:28:04 +08:00
    @xiangyuecn 复制出来的是 gbk 编码的字符转为 utf8 的字符,也就是乱码。用乱码怎么找文件,exists 直接不存在
    string2020
        30
    string2020  
    OP
       2023-03-10 09:32:12 +08:00
    @tramm 转为 utf8 后的 gbk 字符是乱码,java 去访问文件的时候是找不到的,因为系统里没有文件名为乱码的文件。你用的这个文件是你以乱码为名新建的文件。实际我现在的文件是 gbk 编码的文件名,也就是名字只是编码不对,而不是乱码的名字。你在系统里直接建一个文件,用 convmv 转为 gbk ,再用你的方法访问,是访问不到的。
    string2020
        31
    string2020  
    OP
       2023-03-10 09:36:04 +08:00
    @AoEiuV020CN 确实能用 bytes 当文件名就能解决。太蛋疼,把现有文件全部 convmv 了。以后保存文件就保存为 utf8.文件都是 ftp 上来的,ftp 的编码没设置好,导致 window 上来的文件名都是 gbk.现在改成 utf8 后,用 ftp 客户端读取,文件名又全是乱码,需要设置为强制 utf8 才能正常。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2513 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 15:59 · PVG 23:59 · LAX 08:59 · JFK 11:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.