Java文件编码转换工具
由于每个人的项目源码的编码设置不同,造成版本库上的代码下载下来之后中文出现乱码,可以单独在Eclipse里面对每个文件更改编码方式,然后拷贝出内容,在恢复成原来的编码,然后再粘贴内容,就可以去除乱码,但是这样做很麻烦。空闲之余,本人写了个批量转码的工具。
转码实现主要用到 jdk 中的 CharsetEncoder 和 CharsetDecoder.下面贴上代码:
/**
* 实施转码操作
* @author 储玉庭
*/
public class ChannelIO {
private ReadableByteChannel channelIn;
private WritableByteChannel channelOut;
private CharsetEncoder encoder;
private CharsetDecoder decoder;
private ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
private Stack<Byte> reservedBytes = new Stack<Byte>();
private ByteBuffer swapSpace;
private boolean eof;
public ChannelIO(ReadableByteChannel in, WritableByteChannel out,
CharsetEncoder encoder, CharsetDecoder decoder) {
this.channelIn = in;
this.channelOut = out;
this.encoder = encoder;
this.decoder = decoder;
}
public void reset(ReadableByteChannel in, WritableByteChannel out,
CharsetEncoder encoder, CharsetDecoder decoder) {
this.channelIn = in;
this.channelOut = out;
this.encoder = encoder;
this.decoder = decoder;
}
public void reset() {
eof = false;
this.channelIn = null;
this.channelOut = null;
this.encoder = null;
this.decoder = null;
reservedBytes.clear();
buffer.clear();
if (null != swapSpace)
{
swapSpace.clear();
}
}
public void transfer() throws IOException {
long bytesRead = 0;
long tmpBytesRead = 0;
buffer.clear();
while ((tmpBytesRead = fillInBuffer(buffer)) != -1) {
buffer.flip();
byte[] data = encodeLoop(buffer);
channelOut.write(ByteBuffer.wrap(data));
bytesRead += data.length;
buffer.clear();
}
}
private int fillInBuffer(ByteBuffer buffer) throws IOException {
int totalBytesRead = 0;
while (!reservedBytes.isEmpty()) {
buffer.put(reservedBytes.pop());
totalBytesRead++;
}
int bytesRead = channelIn.read(buffer);
if (-1 != bytesRead) {
totalBytesRead += bytesRead;
} else {
eof = true;
}
if (totalBytesRead == 0)
totalBytesRead = -1; // indicates end of file
return totalBytesRead;
}
private byte[] encodeLoop(ByteBuffer bufferData)
throws CharacterCodingException {
CoderResult result = null;
ByteBuffer tmpBuffer = null;
int dataLimit = bufferData.limit();
int preferedSize = dataLimit;
boolean exceptionOccurrerd;
do {
try {
exceptionOccurrerd = false;
CharBuffer cBuffer = decoder.decode(bufferData);
bufferData.flip();
tmpBuffer = allocateBuffer(cBuffer, preferedSize);
result = encoder.encode(cBuffer, tmpBuffer, eof);
tmpBuffer.flip();
preferedSize = preferedSize * 3 / 2;
} catch (Exception e) {
reservedBytes.push(bufferData.get(dataLimit - 1));
bufferData.position(dataLimit - 1);
bufferData.flip();
decoder.reset();
dataLimit = dataLimit - 1;
exceptionOccurrerd = true;
}
} while (null != result && result.isOverflow() || exceptionOccurrerd);
byte[] data = new byte[tmpBuffer.limit()];
tmpBuffer.get(data);
return data;
}
private ByteBuffer allocateBuffer(CharBuffer cBuffer, int preferedSize) {
int totalChars = cBuffer.limit();
int requiredSize = Math.max(
(int) (totalChars * encoder.averageBytesPerChar()),
preferedSize);
if (null == swapSpace || swapSpace.capacity() < requiredSize) {
swapSpace = ByteBuffer.allocate(requiredSize);
}
swapSpace.clear();
return swapSpace;
}
}
/**
* 扫描目录下的所有源码,并调用ChannelIO实施转码
* @author 储玉庭
*/
public class Convertor {
private static ChannelIO transfer = new ChannelIO(null, null, null, null);
private File rootDir;
private String srcEncoding;
private String destEncoding;
public Convertor(File srcDir, String sourceEncoding, String destEncoding)
throws IOException {
if (null == srcDir || !srcDir.exists() || !srcDir.isDirectory()) {
throw new IOException(
"Please specify an existing source file directory!");
}
this.rootDir = srcDir;
this.srcEncoding = sourceEncoding;
this.destEncoding = destEncoding;
}
public final void convert(File[] files) throws IOException {
for (File srcFile : files) {
try {
convert(srcFile);
} catch (IOException e) {
System.err.println("Error occurred while convering file: [" + srcFile + "]");
}
}
}
public final void convert(File file) throws IOException {
if (null == file || !file.exists()) {
throw new IOException("File [" + file.getCanonicalPath()
+ "] does not exist");
}
if (file.isDirectory()) {
convert(file.listFiles());
}
if (!file.getName().endsWith(".java")) {
return;
}
File tmpFile = new File(file.getCanonicalPath() + ".tmp");
ReadableByteChannel channelIn = null;
WritableByteChannel channelOut = null;
boolean succ = false;
try {
channelIn = getChannelForRead(file);
channelOut = getChannelForWrite(tmpFile);
transfer.reset(channelIn, channelOut,
EncodingUtil.getEncoder(destEncoding),
EncodingUtil.getDecoder(srcEncoding));
transfer.transfer();
succ = true;
} finally {
closeChannel(channelIn);
closeChannel(channelOut);
transfer.reset();
if (succ) {
file.delete();
tmpFile.renameTo(file);
}
}
}
private static final void closeChannel(Channel channel) {
if (null != channel) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static final ReadableByteChannel getChannelForRead(File file)
throws FileNotFoundException {
return new FileInputStream(file).getChannel();
}
private static final WritableByteChannel getChannelForWrite(File file)
throws FileNotFoundException {
return new FileOutputStream(file).getChannel();
}
public static void main(String[] args) throws IOException {
if (null == args || args.length < 3) {
System.err
.println("Usage: java Convertor <File Dir> <source encoding> <target encoding>");
System.exit(-1);
}
Convertor convertor = new Convertor(new File(args[0]), args[1], args[2]);
convertor.convert(convertor.rootDir.listFiles());
}
}
/**
* 缓存 CharsetEncoder 和 CharsetDecoder
* @author 储玉庭
*/
public class EncodingUtil {
private static final Map<String, CacheEntry<Charset>> charsetCache = new WeakHashMap<String, CacheEntry<Charset>>(
5);
public static CharsetEncoder getEncoder(String destEncoding) {
Charset cs = findFromCache(destEncoding);
return cs.newEncoder().onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
public static CharsetDecoder getDecoder(String sourceEncoding) {
Charset cs = findFromCache(sourceEncoding);
return cs.newDecoder().onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
private static Charset findFromCache(String encodingName) {
synchronized (charsetCache) {
CacheEntry<Charset> entry = charsetCache.get(encodingName);
if (null == entry) {
entry = new CacheEntry<Charset>();
entry.name = encodingName;
entry.value = Charset.forName(encodingName);
charsetCache.put(encodingName, entry);
}
return entry.value;
}
}
private static final class CacheEntry<V> {
private String name;
private V value;
public String name() {
return name;
}
public V value() {
return value;
}
}
}