使用iconv命令批量原地转码文件
一、iconv简述
日常工作中我们需要将windows生成的文件上传到Linux系统,有时候会因为编码问题出现显示乱码。例如我上传了一个csv文件到Linux服务器上,默认编码为GB2312,在Linux打开则会出现乱码,我们需要将文件进行编码转换。iconv命令对于给定文件把它的内容从一种编码转换成另一种编码。
iconv语法: iconv [OPTION...][FILE…]
参数 | 参数说明 |
---|---|
-f, --from-code=NAME | 指定输入文件编码,把字符从encoding编码开始转换。 |
-t, --to-code=NAME | 指定输出文件编码,把字符转换到encoding编码。 |
-l, --list | 列出已知的编码字符集合 |
-o, --output=FILE | 指定输出文件 |
-c | 忽略输出的非法字符 |
二、iconv原地转码命令
- 命令一:使用find命令查要转码的文件使用iconv命令进行转码(仅linux系统中可用)
#find进行文件查找,使用-exec sh -c 将结果传递给后面的 iconv命令进行转码
find . -name "*.java" -exec sh -c "iconv -f GB18030 -t UTF8 {} -o {}" \;
- 命令二:使用find命令查要转码的文件使用iconv命令进行转码(linux/windows系统均可用)
find . -name "*.java" -exec bash -c 'mkdir -p temp/$(dirname {}); iconv -f gb2312 -t utf-8 {} > temp/{} && mv temp/{} {}' \; && rm -rf temp
注: 该命令会在当前目录下临时创建temp目录 因此temp目录需要是之前不存在的,如果已经存在,则修改命令中temp为其他名称
三、我工作中遇到的问题
问题场景:
工作中遇到生产环境de系统编码不一致情况,我的程序是UTF-8编码,生成环境一个是GBK一个UTF8,使用locale命令在生产环境下查看系统编码结果如下,所以编码不一致的环境中,生成的文件毫无疑问的就中文乱码了,我尝试了在OutputStreamWriter中设置文件编码格式、在中文处使用new String(str.getBatys(),UTF-8)的方式手动进行utf-8的中文编码,最终还有个别字符乱码,“公司” 显示 “公xE5 x8F?” 终结果都差强人意,最终选择将生产的文件使用iconv命令进行一次转码,殊途同归。
# 不乱码的环境编码是utf-8:
$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
# 乱码的环境编码为:
$ locale
LANG=zh_CN.GBK
LC_CTYPE="zh_CN.GBK"
LC_NUMERIC="zh_CN.GBK"
LC_TIME="zh_CN.GBK"
LC_COLLATE="zh_CN.GBK"
LC_MONETARY="zh_CN.GBK"
LC_MESSAGES="zh_CN.GBK"
LC_PAPER="zh_CN.GBK"
LC_NAME="zh_CN.GBK"
LC_ADDRESS="zh_CN.GBK"
LC_TELEPHONE="zh_CN.GBK"
LC_MEASUREMENT=zh_CN.GBK"
LC_IDENTIFICATION="zh_CN.GBK"
LC_ALL=
解决方案:
- 使用执行shell命令的工具类,将生产的文本进行一次转码(已下均是伪代码,仅供参考)
@Value("${transcodingSwitch}")
private Boolean transcodingSwitch; //话单是否需要转码
@Value("${transcodingBefore}")
private String transcodingBefore; //话单转码前的编码格式
@Value("${transcodingLater}")
private String transcodingLater; //话单转码后的编码格式
private boolean iconv(String localFilePath, String localFileName) throws IOException, InterruptedException {
//拼接iconv转码命令
String iconvCmd = "find " + localFilePath + " -name " + localFileName + " -exec sh -c \"iconv -f " +
transcodingBefore + " -t " + transcodingLater + " {} -o {}\" \\;";
return ExecUtils.exec(iconvCmd);
}
//测试转码
public void testIconv(){
if (transcodingSwitch) {
if (!iconv(transferObject.getLocalFilePath(), localFileName))
logger.error("Error: " + localFileName + "转码失败!!!");
}
}
- 执行shell命令工具类(模板拿来即用)
import org.apache.log4j.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class ExecUtils {
public static Logger logger = Logger.getLogger(ExecUtils.class);
private static final ExecutorService THREAD_POOL = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
public static boolean exec(String cmd) throws IOException, InterruptedException {
String[] cmds = {"/bin/sh", "-c", cmd};
Process process = Runtime.getRuntime().exec(cmds);
//消费正常日志
StringBuffer result = clearStream(process.getInputStream());
//消费错误日志
StringBuffer errorInfo = clearStream(process.getErrorStream());
//i为返回值,判断是否执行成功
int i = process.waitFor();
if (i != 0) {
return false;
}
return true;
}
private static StringBuffer clearStream(final InputStream stream) {
final StringBuffer result = new StringBuffer();
//处理buffer的线程
THREAD_POOL.execute(new Runnable() {
@Override
public void run() {
String line;
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(stream));
while ((line = in.readLine()) != null) {
result.append(line).append("\n");
}
} catch (IOException e) {
logger.error("error exec shell.", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignored) {
}
}
}
}
});
return result;
}
}