记录英语单词时,想把英语和中文翻译分别对齐,有些人写代码喜欢把变量按这种方式对齐。在网上没搜到相关方法,于是自己试着写代码去实现,原本以为很简单,写的时候才发现有不少问题。先看效果:
普通的
对齐前:
对齐后:
发挥点创意
对齐前:
对齐后:
实现
实现的思路比较简单,读取文本文件,按正则分割,找出最长的部分,补齐空格,输出。
看起来相当简单,花了一个多小时,就写出来了,马上运行,发现输出一团糟,去数每个部分的字符数,个数是一样的,网上一搜,原来跟字体有关系,好吧,那换个等宽的字体。换好字体后有些地方已经对齐了,有些地方还是没对齐,发现是中文的问题,中文宽度与英文宽度不相同,于是首先根据正则去判断字符是中文还是英文,然后自己实现计算字符长度的方法,在判断中文字符上折腾了许久,因为标点符号等等都要考虑进去,反正是来来回回试了好久,对Unicode编码范围不熟悉,没办法。终于,好像都搞定了,反复测试,突然发现第一行的对齐少了一个空格,尼玛,这是怎么回事啊,Debug发现第一行的最开始有一个奇怪的字符"\uFEFF",这他妈是什么鬼,上网搜,发现是Unicode编码的什么鬼BOM头,好吧,不管它是什么鬼,直接把它去掉了……
反正是遇到了各种各样的问题,越到后面心里越没底了,与字符集相关的问题实在是太头疼了,而且我根本就没去处理编码的问题,所以文本的编码需要和IDE的编码保持一致,否则就会产生乱码。我也就这样算了,以下是JAVA代码实现。
源码
因为看过《重构》和《代码整洁之道》,写代码时时刻想着要写干净点,扩展性强点,经过反复修改,最终自己觉得还行吧,当然,肯定有不少值得改进的地方,现在就这样吧。
App.java
1 package textalign; 2 3 import java.io.IOException; 4 5 /** 6 * @author tingl 7 * @version 2017/9/27 8 */ 9 public class App { 10 11 public static void main(String[] args) { 12 long start = System.currentTimeMillis(); 13 14 String filePath = "C:\\Users\\tingl\\Desktop\\Test2.txt"; 15 TextAlign textAlign = new TextAlign(/*",|。|,|[.]|( {2,})|\t| +"*/); 16 17 if (args.length > 0) { 18 filePath = args[0]; 19 } 20 try { 21 textAlign.align(filePath); 22 } catch (IOException e) { 23 e.printStackTrace(); 24 } 25 26 System.out.println(System.currentTimeMillis() - start); 27 } 28 }
TextAlign.java
1 package textalign; 2 3 import java.io.IOException; 4 import java.util.List; 5 import java.util.regex.Pattern; 6 7 /** 8 * @author tingl 9 * @version 2017/9/27 10 */ 11 public class TextAlign { 12 private static final String CHINESE_CHARACTER = "[\u4e00-\u9fa5]|[\uFE30-\uFFA0]|[\u3000-\u303F]"; 13 private static final Pattern CHINESE_CHARACTER_PATTERN = Pattern.compile(CHINESE_CHARACTER); 14 private static final int SEPARATE_SPACE_AMOUNT = 4; 15 16 private TextAlignFileUtil textAlignFileUtil; 17 private List<String[]> textLines; 18 private int[] longestBlockLengths; 19 20 public TextAlign() { 21 textAlignFileUtil = new TextAlignFileUtil(); 22 } 23 24 public TextAlign(String spiltRegex) { 25 textAlignFileUtil = new TextAlignFileUtil(spiltRegex); 26 } 27 28 public void align(String filePath) throws IOException { 29 textLines = textAlignFileUtil.readToList(filePath); 30 initLongestBlockLengths(); 31 fillTextLinesBySpaces(); 32 textAlignFileUtil.write(); 33 } 34 35 private void initLongestBlockLengths() { 36 int longestArrayLength = 0; 37 for (String[] blocks : textLines) { 38 if (blocks.length > longestArrayLength) { 39 longestArrayLength = blocks.length; 40 } 41 } 42 longestBlockLengths = new int[longestArrayLength]; 43 fillLongestBlockLengths(); 44 } 45 46 private void fillLongestBlockLengths() { 47 for (String[] blocks : textLines) { 48 if (blocks.length < 2) continue; 49 for (int i = 0; i < blocks.length; i++) { 50 int length = stringLengthFitWidth(blocks[i]); 51 if (length > longestBlockLengths[i]) { 52 longestBlockLengths[i] = length; 53 } 54 } 55 } 56 } 57 58 private int stringLengthFitWidth(String s) { 59 if (!CHINESE_CHARACTER_PATTERN.matcher(s).find()) { 60 return s.length(); 61 } 62 int length = 0; 63 for (String c : s.split("")) { 64 if (CHINESE_CHARACTER_PATTERN.matcher(c).find()) { 65 length++; 66 } 67 length++; 68 } 69 return length; 70 } 71 72 private void fillTextLinesBySpaces() { 73 for (String[] blocks : textLines) { 74 for (int i = 0; i < blocks.length - 1; i++) { 75 String block = blocks[i]; 76 int spaceAmount = longestBlockLengths[i] - stringLengthFitWidth(block) + SEPARATE_SPACE_AMOUNT; 77 blocks[i] = block + spaces(spaceAmount); 78 } 79 } 80 } 81 82 private String spaces(int spaceAmount) { 83 StringBuilder spaces = new StringBuilder(); 84 for (int i = 0; i < spaceAmount; i++) { 85 spaces.append(" "); 86 } 87 return spaces.toString(); 88 } 89 }
TextAlignFileUtil.java
1 package textalign; 2 3 import java.io.*; 4 import java.util.ArrayList; 5 import java.util.Arrays; 6 import java.util.List; 7 8 /** 9 * @author tingl 10 * @version 2017/9/27 11 */ 12 class TextAlignFileUtil { 13 private static final String FILENAME_POSTFIX = "_aligned"; 14 private String spiltRegex = "( {2,})|\t"; 15 private List<String[]> textLines; 16 private String outPath; 17 18 TextAlignFileUtil() { 19 } 20 21 TextAlignFileUtil(String spiltRegex) { 22 this.spiltRegex = spiltRegex; 23 } 24 25 List<String[]> readToList(String path) throws IOException { 26 File file = new File(path); 27 return readToList(file); 28 } 29 30 private List<String[]> readToList(File file) throws IOException { 31 getOutPath(file.getAbsolutePath()); 32 BufferedReader reader = new BufferedReader(new FileReader(file)); 33 textLines = new ArrayList<>(); 34 String line; 35 while ((line = reader.readLine()) != null) { 36 textLines.add(removeEmptyAndTrim(line.split(spiltRegex))); 37 } 38 reader.close(); 39 removeBomHead(); 40 return textLines; 41 } 42 43 private void getOutPath(String srcPath) { 44 int dotPosition = srcPath.lastIndexOf("."); 45 outPath = srcPath.substring(0, dotPosition) + FILENAME_POSTFIX + srcPath.substring(dotPosition); 46 if (new File(outPath).exists()) { 47 getOutPath(outPath); 48 } 49 } 50 51 private String[] removeEmptyAndTrim(String[] src) { 52 for (int i = 0; i < src.length; i++) { 53 src[i] = src[i].trim(); 54 } 55 56 List<String> dest = new ArrayList<>(Arrays.asList(src)); 57 dest.removeIf(String::isEmpty); 58 return dest.toArray(new String[0]); 59 } 60 61 private void removeBomHead() { 62 String[] blocks = textLines.get(0); 63 blocks[0] = blocks[0].replace("\uFEFF", ""); 64 } 65 66 void write() throws IOException { 67 BufferedWriter writer = new BufferedWriter(new FileWriter(outPath)); 68 for (String[] blocks : textLines) { 69 writer.write(getLine(blocks)); 70 writer.newLine(); 71 writer.flush(); 72 } 73 writer.close(); 74 } 75 76 private String getLine(String[] blocks) { 77 StringBuilder sb = new StringBuilder(); 78 for (String block : blocks) { 79 sb.append(block); 80 } 81 return sb.toString(); 82 } 83 }