使用java操作Excel替换xml中的翻译文案

一、概述

海外APP,经常会翻译国外语言,项目中间突然加入一门新语言,一般海外市场部会提供一个翻译好的Excel表格,里边包含了所有的字符串和对应的翻译,如下所示:

如果项目不断迭代,对应的字符串也可能会有N多个表。手动翻译翻译,需要在项目的strings.xml文件和Excel表格中来回切换,逐条搜索替换,很不方便,使用java语言,来操作Excel表格,代码处理,可以减少了繁琐劳动,降低出错率。

需要考虑的问题很多,大致如下:

1.使用java,怎么操作Excel文件,不仅要读取,匹配好的数据也要放入一个表格里,此时就涉及到了写

2.操作时,对Excel文件的格式有个要求,不同格式的Excel文件处理时,有何不同

3.第一次处理,共用到三个Excel文件:

    1). 翻译好的文件,即源文件,ori.xls。里边都是单纯的翻译,不包含这种样式。

    2). 复制strings.xml内容到Excel文件中,需要比对的文件,des.xls。它是含有contentStr的字符串。

    3). 以翻译乌克兰语为例,得到ori.xls里C、Q两列的内容后,再获取des.xls中每个单元格的内容,解析出nameStr和contentStr。根据contentStr,和ori.xls里的结果匹配比较,找到contentStr在ori.xls中的行列坐标,这样就可以推知乌克兰语的行列坐标。重新组装为translate content,放入结果文件result.xls中。

4. 此时要考虑des.xls中每条string的内容格式和结果文件result.xls的内容格式。

    1).怎么截取name和content部分,最终格式为: translate content

    2).该条若注释掉了,直接跳过

    3).获取到content后,要判断是否被双引号""包围,若没被双引号包围,出现了 ' 要转义为 \'

    4).content中若包含非英语形式的特殊符号,如?!-等,要替换处理

    5). Excel文件中的&在xml中表示为 &,也要处理

等等。

二、读Excel文件

// 创建file对象
File file = new File(filePath);
// 创建输入流,读取Excel
InputStream is = new FileInputStream(file);
// jxl提供的Workbook类
Workbook wb = Workbook.getWorkbook(is);
//获取第一个sheet
Sheet sheet = wb.getSheet(0);
Log.d(TAG, "readOriExcel: sheet name = "+ sheet.getName());
// 得到所有的行数
int rows = sheet.getRows();
for (int j = 0; j < rows; j++) {
	// 得到这行每个单元格的数据
	Cell[] cells = sheet.getRow(j);
}

三、写Excel文件

/**
 * 将数据写入到excel中
 */
public static  void writeExcel() {
	//1 创建一个workbook对应一个excel文件
	HSSFWorkbook workbook = new HSSFWorkbook();
	//2 在workbook中创建一个sheet对应excel中的sheet
	HSSFSheet sheet = workbook.createSheet(name);

	for (int i = 0; i < desSheetAllData.size(); i++) {
		HSSFRow row = sheet.createRow(i);
		List rowData = desSheetAllData.get(i);
		for (int j = 0; j < rowData.size(); j++) {
			//3 创建单元格并设值
			row.createCell(j).setCellValue(rowData.get(j));
		}
	}

	FileOutputStream fos = null;
	try {
		fos = new FileOutputStream(PATH+"/result.xls");
		workbook.write(fos);
	} catch (IOException e) {
		Log.d(TAG, "writeExcel: catch - " + e.getMessage());
	} finally {
		try {
			//数据量较大,使用flush刷新该流的缓冲区
			//一般写字符时要用,因为字符是先进入的缓冲区
			fos.flush();
			//关闭流对象
			fos.close();
		} catch (IOException e) {
			Log.d(TAG, "writeExcel: finally -" + e.getMessage());
		}
	}
	Log.d(TAG, "writeExcel: success!!!");
}

注:此处使用HSSFWorkbook创建的是xls文件,若创建xlsx文件,要用XSSFWorkbook。 HSSFWorkbook和XSSFWorkbook 类都实现了Workbook接口。

四、解析字符串 contentStr

1.利用正则表达式"<[^>]+>"解析出contentStr。

String str = "contentStr";
// 定义xml标签的正则表达式
String regExp = "<[^>]+>";
// 过滤xml标签
String contentStr = Pattern.compile(regExp).matcher(str).replaceAll("");

2. 利用"\">"来解析出String[] split = str.split("\">"); String nameStr = split[0];

根据contentStr找到对应的翻译以后,再把translate content

五、处理要替换的内容contentStr

1. 汉语中的?!- 等,使用replaceAll()方法。注意,调用replaceAll()方法的使用方式:

content = content.replaceAll("\'", "'");

必须接收一次replaceAll返回的字符串,这样,才是处理后,想要的结果。

2. 若想得到某个字符,如-的ASIIC码用(int)('-')就可以得到。

3. 去掉首尾的引号,使用substring()方法。

content = content.substring(1, content.length() - 1);//去掉首尾的引号

str.length()-1为字符串最后一位的下标,它作为substring()的参数2,为开区间,表示不包含最后一位。参数1位闭区间,包含第一位。故:

String test = "0123456";
Log.d(TAG, "lym12345: " + test.substring(0, test.length()-1));// --> 012345
Log.d(TAG, "lym12345: " + test.substring(1, test.length()-2));// --> 1234

4. 若contentStr中含有\",比如:open \"location\",在英语和乌克兰语匹配过程中,是以open "location"来匹配的,此时要把\"替换为",即把\去掉

//处理句中的\"
if (content.contains("\\\"")){
	content = content.replaceAll("\\\\", "");
}

java中,\用\\表示,"用\"表示,\"则用\\\"表示。正则表达式中"\\\\"表示\。

\' 替换 ',\" 替换 ":

//没有双引号的时候,遇到'再用\'替换,遇到"再用\"替换
if (content.contains("'")) {
	content = content.replace("'", "\\'");
} else if (content.contains("\"")){
	content = content.replace("\"", "\\\"");
}

5. 去除字符串前后的空格,java的string.trim()只能去英文半角空格

public class StringUtil {
 
    /**普通的英文半角空格Unicode编码*/
    private static final int SPACE_32 = 32;
 
    /**中文全角空格Unicode编码(一个中文宽度)*/
    private static final int SPACE_12288 = 12288;
 
    /**普通的英文半角空格但不换行Unicode编码(==   ==   == no-break space)*/
    private static final int SPACE_160 = 160;
 
    /**半个中文宽度(==   == en空格)*/
    private static final int SPACE_8194 = 8194;
 
    /**一个中文宽度(==   == em空格)*/
    private static final int SPACE_8195 = 8195;
 
    /**四分之一中文宽度(四分之一em空格)*/
    private static final int SPACE_8197 = 8197;
 
    /**窄空格*/
    private static final int SPACE_8201 = 8201;
 
    /**
     * 去除字符串前后的空格, 包括半角空格和全角空格(中文)等各种空格, java的string.trim()只能去英文半角空格
     * @param str
     */
    public static String trim(String str) {
        if (TextUtils.isEmpty(str)) {
            return str;
        }
 
        char[] val = str.toCharArray();
        int st = 0;
        int len=val.length;
        while ((st < len) && isSpace(val[st])) {
            st++;
        }
        while ((st < len) && isSpace(val[len - 1])) {
            len--;
        }
        return ((st > 0) || (len < val.length)) ? str.substring(st, len) : str;
    }
 
    private static boolean isSpace(char aChar) {
        return aChar == SPACE_32 || aChar == SPACE_12288 || aChar == SPACE_160 || aChar == SPACE_8194
                || aChar == SPACE_8195 || aChar == SPACE_8197 || aChar == SPACE_8201;
    }
}

六、其他

1. 每次写入时有上限,不要多余150行;

2. 为便于查看,Excel中不要空行;

3. 读写SD卡里的文件,打开操作后的Excel,提示: 无法打开文件,可以尝试重启手机、AndroidStudio

七、参考

HSSFworkbook,XSSFworkbook,SXSSFworkbook区别总结

Java 正则匹配html标签

JAVA 正则表达式 (超详细)

正则表达式之分组 group(java版)

浅谈JAVA中流的flush()&close()方法

close()和flush()的区别

八、代码附上

public class Excel {
    private static final String TAG = "lym1234";
    private static String PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/xml/";
    private static String DES_FILE = "des.xls";
    private static String ORI_FILE = "ori.xls";
    private static int indexR = 0;
    private static int indexC = 0;

    private static final int DOT = 1;//.
    private static final int QUESTION_MARK = 2;//?
    private static final int POINT = 3;//!
    
    private static List> oriSheetAllData = new ArrayList<>(); // ori所有的数据
    private static List> desSheetAllData = new ArrayList<>(); // des所有的数据
    
    public static void operate() {
        try {
            oriSheetAllData.clear();
            desSheetAllData.clear();
            readOriExcel();
            readDesExcel();
            writeExcel();
        } catch (Exception e) {
            Log.d(TAG, "e: " + e.getMessage());
        }
    }
    
    private static void readOriExcel() throws Exception {
        // 读取Excel文件
        File file = new File(PATH+ORI_FILE);
        // 创建输入流,读取Excel
        InputStream is = new FileInputStream(file.getAbsolutePath());
        // jxl提供的Workbook类
        Workbook wb = Workbook.getWorkbook(is);
        int sheetNum = wb.getNumberOfSheets();
        Log.d(TAG, "readOriExcel: sheetNum = " + sheetNum);

        for (int i = 0; i < sheetNum; i++){
            Sheet sheet = wb.getSheet(i);//获取每个sheet
            Log.d(TAG, "readOriExcel: sheet name = "+ sheet.getName());
            // 得到所有的行数
            int rows = sheet.getRows();
            Log.d(TAG, "readOriExcel: rows = " + rows);

            // 越过第一行 它是列名称
            for (int j = 1; j < rows; j++) {
                List oneData = new ArrayList<>();
                // 得到每一行的单元格的数据
                Cell[] cells = sheet.getRow(j);
                Log.d(TAG, "readOriExcel: cols = " + cells.length);
                for (int k = 0; k < cells.length; k++) {
                    // 读取每行的第3/17/18列元素
                    // 若Excel表格中,第5列的数据全部为空,则第一行的第5个单元格要填上数据,不能为空。
                    // 否则,从第二行开始,每行下标为4的数据应该为空,就会变成第6个单元格的内容,
                    // 且读取的总长度会减1
                    if (2 == k || 16 == k || 17 == k) {
                        oneData.add(StringUtil.trim(cells[k].getContents()));
                    }
                }
                // 存储每一条数据
                oriSheetAllData.add(oneData);
            }
        }
    }

    private static void readDesExcel() throws  Exception{
        boolean beginWithDoubleQuotes = false;//被双引号包围
        boolean haveDoubleQuotes = false;//句中有双引号
        int endWithSpecialChar = 0;

        // 读取Excel文件
        File file = new File(PATH+DES_FILE);
        // 创建输入流,读取Excel
        InputStream is = new FileInputStream(file.getAbsolutePath());
        // jxl提供的Workbook类
        Workbook wb = Workbook.getWorkbook(is);
        Sheet sheet = wb.getSheet(0);//获取每个sheet
        Log.d(TAG, "readOriExcel: sheet name = "+ sheet.getName());
        // 得到所有的行数
        int rows = sheet.getRows();
        Log.d(TAG, "readOriExcel: rows = " + rows);
        Log.d(TAG, "readDesExcel: " + (int)('–') + " : " + (int)('-'));
        for (int j = 0; j < rows; j++) {
            List oneData = new ArrayList<>();
            // 得到每一行的单元格的数据
            Cell[] cells = sheet.getRow(j);
            if (0 == cells.length){//空的单元格
                oneData.add(" ");
                oneData.add(" ");
                continue;
            }
            //Log.d(TAG, "readOriExcel: cols = " + cells.length);
            for (int k = 0; k < 1/*cells.length*/; k++) {
                String str = StringUtil.trim(cells[k].getContents().trim());
                if (str.startsWith("
                    

你可能感兴趣的:(android,java)