使用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=

解决方案:

  1. 使用执行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 {}\" \\;";
           String iconvCmd = "find " + localFilePath + " -name " + localFileName +" -exec bash -c 'mkdir -p temp/$(dirname {}); iconv -f " + transcodingBefore + " -t " + transcodingLater + " {} > temp/{} && mv temp/{} {}' \\; && rm -rf temp";
           return ExecUtils.exec(iconvCmd);
        }

	//测试转码
	public void testIconv(){
  		if (transcodingSwitch) {
            if (!iconv(transferObject.getLocalFilePath(), localFileName))
              logger.error("Error: " + localFileName + "转码失败!!!");
     	}	
	}
  1. 执行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;
    }
}
posted @ 2023-03-01 23:06  lihewei  阅读(573)  评论(0编辑  收藏  举报
-->