将ANSI转换为UTF-8& java.lang.OutOfMemoryError:Java堆空间

时间:2018-05-14 16:16:05

标签: java utf-8 heap ansi

我的最终目标是将文件从ANSI转换为UTF-8。为此,我在Java中使用了一些代码:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ConvertFromAnsiToUtf8 {

    public static void main(String[] args) throws IOException {

        try {
            Path p = Paths.get("C:\\shared_to_vm\\test_encode\\test.csv");
            ByteBuffer bb = ByteBuffer.wrap(Files.readAllBytes(p));
            CharBuffer cb = Charset.forName("windows-1252").decode(bb);
            bb = Charset.forName("UTF-8").encode(cb);
            Files.write(p, bb.array());
        } catch (Exception e) {
            System.out.println(e);
        } 

    } 

}

当我在小文件上测试代码时,代码工作正常。我的文件从ANSI转换为UTF-8,所有字符都被识别并编码良好。但是当我尝试在我需要转换的文件上使用它时,我得到错误java.lang.OutOfMemoryError:Java堆空间。

据我所知,我的文件中有150万行,所以我很确定我用我的应用程序创建了太多的对象。

当然,我已经检查了这个错误意味着什么以及如何解决它(例如herehere)但是提高我的JVM的内存容量是解决它的唯一方法?如果是的话,我还应该使用多少?

任何形式的帮助(提示,建议,链接或其他)将不胜感激!

3 个答案:

答案 0 :(得分:0)

不要一次阅读整个文件:

ByteBuffer bb = ByteBuffer.wrap(Files.readAllBytes(p));

相反,尝试逐行阅读:

Files.lines(p, Charset.forName("windows-1252")).forEach(line -> {
   // Convert your line, write to file
});

答案 1 :(得分:0)

如果你有一个大的文件,比可用的随机存取内存大,你应该逐块转换字符。

您可以找到以下示例:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class Iconv {

    private static void iconv(Charset toCode, Charset fromCode, Path src, Path dst) throws IOException {
        CharsetDecoder decoder = fromCode.newDecoder();
        CharsetEncoder encoder = toCode.newEncoder();
        try (ReadableByteChannel source = FileChannel.open(src, StandardOpenOption.READ);
                WritableByteChannel destination = FileChannel.open(dst, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING,
                        StandardOpenOption.WRITE);) {
            ByteBuffer readBytes = ByteBuffer.allocate(4096);
            while (source.read(readBytes) > 0) {
                readBytes.flip();
                destination.write(encoder.encode(decoder.decode(readBytes)));
                readBytes.clear();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        iconv(Charset.forName("UTF-8"), Charset.forName("Windows-1252"), Paths.get("test.csv") , Paths.get("test-utf8.csv") );
    }

}

答案 2 :(得分:0)

流式输入,转换字符编码,然后随时写入输出。这样,您不需要将整个文件读入内存,而只需将其读取到您想要的内容。

如果要最小化(慢速)系统调用的次数,可以使用类似的方法,但显式创建具有更大内部缓冲区的BufferedInputStream,然后将其包装在InputStreamReader中。但是这里显示的简单方法不太可能成为许多应用中的关键点。

private static final Charset WINDOWS1252 = Charset.forName("windows-1252");

private static final int DEFAULT_BUF_SIZE = 8192;

public static void transcode(Path input, Path output) throws IOException {
    try (Reader r = Files.newBufferedReader(input, WINDOWS1252);
         Writer w = Files.newBufferedWriter(output, StandardCharsets.UTF_8, StandardOpenOption.CREATE_NEW)) {
        char[] buf = new char[DEFAULT_BUF_SIZE];
        while (true) {
            int n = r.read(buf);
            if (n < 0) break;
            w.write(buf, 0, n);
        }
    }
}