当我使用Shift-JIS字符集用Java 8创建文件时,一些字符会被替换成问号'?'。

3

当我使用Shift-JIS字符集创建文件时,遇到了一个问题。

这是我想写入txt文件的文本示例:

繰戻_日経選挙システム保守2019年1月10日~;[2019年度更新]横浜第1DCコロケ―ション(2ラック)

使用Shift-JIS字符集,我发现在文件中有两个“?”而不是“~”和“―”:

繰戻_日経選挙システム保守2019年1月10日?;[2019年度更新]横浜第1DCコロケ?ション(2ラック)

使用UTF-8字符集,则完全正确:

繰戻_日経選挙システム保守2019年1月10日~;[2019年度更新]横浜第1DCコロケ―ション(2ラック)

这是我的代码:

package it.grupposervizi.easy.ef.etl.elaboration;

import com.nimbusds.jose.util.StandardCharset;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.FileUtils;

public class TestShiftJIS {

  private static final String TEXT = "繰戻_日経選挙システム保守2019年1月10日~;[2019年度更新]横浜第1DCコロケ―ション(2ラック)";
  private static final String DIRECTORY = "C:\\temp\\japan\\";
  private static final String SHIFT_JIS = "Shift-JIS";
  private static final String UTF_8 = StandardCharset.UTF_8.name();
  private static final String EXTENSION = ".txt";

  public static void main(String[] args) {

    final List<String> charsets = Arrays.asList(SHIFT_JIS, UTF_8);
    charsets.forEach(c -> {
      final String fName = DIRECTORY + c + EXTENSION;
      File file = new File(fName);
      try {
        FileUtils.writeStringToFile(file, TEXT, Charset.forName(c));
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    });

    System.out.println("End Test");
  }
}

你知道为什么这两个字符没有包含在Shift-JIS字符集中吗?


你使用的文件编辑器可能无法显示这些字符,是这个原因吗? - assylias
1
可疑字符是(DashPunctuation) U+2015 水平线 和(MathSymbol) U+FF5E 全角波浪号。我怀疑这些字符是否在Shift-JIS中... - JosefZ
3个回答

1

///编辑:

您试图保存具有不同于默认编码的罕见编码方式的文件。尝试更改字符编码。 更多关于编码的信息» https://en.wikipedia.org/wiki/Character_encoding

///

尝试使用:Charset.forName("CP943C")

1
你能否编辑你的回答并解释一下为什么这行代码可以解决这个问题?解释它是如何解决问题的将有助于提高你的帖子质量,可能会得到更多的赞。 - HardcoreGamer
请解释你的答案。OP 不仅应该得到解决方案,还应该知道为什么它是正确的。 - Koziołek
我尝试使用Charset.forName("CP943C"),但没有成功。目前我解决方案是始终使用UTF8。 - Giulio Andolfi

0

正如@Marcono1234所回答的那样,Java中的Shift-JIS映射不支持(U+FF5E)和(U+FF5E)。要将这些代码点从UTF-8映射到Shift-JIS编码,您必须使用Charset.forName("windows-31j");Charset.forName("MS932");而不是Charset.forName("Shift-JIS");


0

@JosefZ 已经基本上给出了答案:Shift-JIS 不支持 (U+FF5E)和 (U+FF5E)。

这可以使用 Charset.newEncoder().canEncode(char) 进行验证:

public class ShiftJisTest {
    public static void main(String[] args) {
        // 繰戻_日経選挙システム保守2019年1月10日~;[2019年度更新]横浜第1DCコロケ―ション(2ラック)
        String s = "\u7e70\u623b\u005f\u65e5\u7d4c\u9078\u6319\u30b7\u30b9\u30c6\u30e0\u4fdd\u5b88\u0032\u0030\u0031\u0039\u5e74\u0031\u6708\u0031\u0030\u65e5\uff5e\u003b\u005b\u0032\u0030\u0031\u0039\u5e74\u5ea6\u66f4\u65b0\u005d\u6a2a\u6d5c\u7b2c\uff11\u0044\u0043\u30b3\u30ed\u30b1\u2015\u30b7\u30e7\u30f3\uff08\uff12\u30e9\u30c3\u30af\uff09";
        Charset charset = Charset.forName("Shift-JIS");
        for (char c : s.toCharArray()) {
            CharsetEncoder encoder = charset.newEncoder();
            if (!encoder.canEncode(c)) {
                System.out.printf("%s (U+%04X)%n", c, (int) c);
            }
        }
        
        try {
            charset.newEncoder().encode(CharBuffer.wrap(s));
        } catch (CharacterCodingException e) {
            // java.nio.charset.UnmappableCharacterException: Input length = 1
            e.printStackTrace();
        }
    }
}

你看到 ? 的原因是因为 Apache Commons IO 的 FileUtils.writeStringToFile(File, String, Charset) 在内部使用了 String.getBytes(Charset)12),它的 documentation 表示:

[...] 该方法总是使用此字符集的默认替换字节数组替换格式不正确的输入和无法映射的字符序列。

而 CharsetEncoder documentation 则表示:

[...] 替换初始设置为编码器的默认替换,通常情况下(但并不总是)具有初始值{(字节)'?'}


谢谢你的回答。我尝试了你的代码,两个字符都是用Shift-JIS编码的。 我尝试使用另一种方法来写文件,但没有成功。 我会再试一次。 - Giulio Andolfi
@GiulioAndolfi 你说的“两个字符是用Shift-JIS编码”的意思是什么?我的代码旨在证明它们无法被编码。 - Marcono1234

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接