当我使用Shift-JIS字符集使用Java 8创建文件时,某些字符用char'?'代替

问题描述

使用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字符集中?

解决方法

@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)内部(12)使用String.getBytes(Charset),而{{3}说:

[...]此方法始终使用此字符集的默认替换字节数组替换格式错误的输入和不可映射的字符序列。

CharsetEncoder documentation说:

[...]替换最初设置为编码器的默认替换,该替换通常(但并非总是)具有初始值{ (byte)'?' }

,

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

,

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