如何使用java从US-ASCII文本中读取斯堪的纳维亚字符

时间:2013-12-11 22:28:04

标签: java spring encoding character-encoding spring-batch

我正在编写Spring Batch程序,它在每行读入包含固定长度数据的文件并将值写入数据库。问题是这个文件的编码是US-ASCII,但它有scandinavic字母,最终是##或其他一些奇怪的字符。斯堪的纳维奇字符编码为0x5B =Ä,0x5C =Ö,0x5D =Å。否则它是大写的US-ASCII文本。

在此文件中阅读的最佳方法是什么?我目前正在使用随弹簧批量提供的FlatFileItemReader。

2 个答案:

答案 0 :(得分:2)

我遇到了类似的问题,其中文本应该被编码为ASCII但是使用了国际字符。我的解决方案是使用Windows-1252编码读取字节。

Javadoc我看到FlatFileItemReader有一个setEncoding方法。请尝试以下方法:

reader.setEncoding("Windows-1252");

并查看它是否能提供更好的结果。

答案 1 :(得分:1)

我猜这个字符集是ISO646-SE / -FI(7位),如http://www.aivosto.com/vbtips/charsets-7bit.html中所述。 Java似乎并不支持开箱即用。您可以实现自定义Charset。我不知道Spring,但是从JavaDoc来看,您可能必须在BufferedReaderFactory上设置FlatFileItemReader

以下代码可能是一个起点:

public static void main(String[] args) {
    try {
        InputStream is = new ByteArrayInputStream(new byte[]{ 0x5B, 0x5C, 0x5D });
        BufferedReader br = new BufferedReader(new InputStreamReader(is, new ISO646SECharset()));
        // Spring: set custom BufferedReaderFactory returning BufferedReader like above line?
        System.out.println(br.readLine());
        br.close();
    } catch (Exception ex) {
        ex.printStackTrace(System.err);
    }
}

/** ISO646-SE/-FI 7-bit character set. */
public static class ISO646SECharset extends Charset {

    private static final char[] b2c = new char[0x80];
    private static final Map<Character, Byte> c2b = new HashMap<>(0x80);
    static {
        for (int i = 0; i < b2c.length; i++) { b2c[i] = (char) i; }
        // see http://www.aivosto.com/vbtips/charsets-7bit.html
        // see http://www.utf8-zeichentabelle.de/
        b2c[0x24] = '\u00A4';
        b2c[0x5B] = '\u00C4';
        b2c[0x5C] = '\u00D6';
        b2c[0x5D] = '\u00C5';
        b2c[0x7B] = '\u00E4';
        b2c[0x7C] = '\u00F6';
        b2c[0x7D] = '\u00E5';
        b2c[0x7E] = '\u00AF';
        for (int i = 0; i < b2c.length; i++) { c2b.put(b2c[i], (byte) i); }
    }

    protected ISO646SECharset() {
        super("ISO646-SE", new String[]{ "ISO646-FI" });
    }

    @Override
    public boolean contains(Charset cs) {
        return false;
    }

    @Override
    public CharsetDecoder newDecoder() {
        return new CharsetDecoder(ISO646SECharset.this, 1.0f, 1.0f) {
            @Override
            protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
                while (true) { // TODO optimize, see US_ASCII class of OpenJDK
                    if (in.remaining() <= 0) return CoderResult.UNDERFLOW;
                    if (out.remaining() <= 0) return CoderResult.OVERFLOW;
                    byte b = in.get();
                    if ((b & 0x80) != 0) b = (byte) '?';
                    out.put(b2c[b & 0x7F]);
                }
            }
        };
    }

    @Override
    public CharsetEncoder newEncoder() {
        return new CharsetEncoder(ISO646SECharset.this, 1.0f, 1.0f) {
            @Override
            protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
                while (true) {
                    if (in.remaining() <= 0) return CoderResult.UNDERFLOW;
                    if (out.remaining() <= 0) return CoderResult.OVERFLOW;
                    Byte b = c2b.get(in.get());
                    out.put(b != null ? b : (byte) '?');
                }
            }
        };
    }
}

修改:在How to define a new Charset in Java/Android?中,他们提到如何通过定义允许Charset使用的CharsetProvider来注册setEncoding('ISO646-SE')