日前接到一个Case:需要将一个源字符串中的某些字符按指定的规则替换成新的字符
目前规则主要有两种:1、新字符串替换旧字符串;2、两个字符串互换
遇到的问题:用一般的string.replace()方法,有可能会出现第一步替换的新字符会在接下来的替换中被当作原有的旧字符,从而被再次替换。
EXP:源字符串“ABCD”, 规则1:A->C; 规则2:C->D; 正确结果:CBDD 错误结果:DBDD
解决方式一:罕见字符法
主要思路:使用罕见字符(exp:"@#$$#@")作为中间替换步骤
Step1:将需要替换的oldchar全部替换为“@#$oldchar$#@”
Step2:将“@#$oldchar$#@”再全部替换为newchar
具体代码:
1 /** 2 * 替换字符串 3 * @param sOldString 需要替换的源字符串 4 * @param listReplaceRule 替换规则 5 * @param sOldWord 要替换的字符串 6 * @param sNewWord 替换后的字符串 7 * @throws IOException 8 9 public static String replaceSring(String sOldString,ArrayList listReplaceRule) throws IOException {10 String sOldWord = "";11 String sNewWord = "";12 String sRule = "";13 //Step 1: 通过罕见字符串:"@#$$#@","&*%%*&"将sOldWord,sNewWord位置互换14 for (int i = 0; i < listReplaceRule.size(); i++) {15 sOldWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("oldword"));16 sNewWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("newword"));17 sRule = uFunc.getString(((Map) listReplaceRule.get(i)).get("rule"));18 if ("2".equals(sRule) && sOldString.indexOf(sOldWord) >= 0 && sOldString.indexOf(sNewWord) >= 0) {19 sOldString = sOldString.replace(sOldWord, "@#$$#@");20 sOldString = sOldString.replace(sNewWord, "&*%%*&");21 sOldString = sOldString.replace("@#$$#@", sNewWord);22 sOldString = sOldString.replace("&*%%*&", sOldWord);23 }24 }25 //Step 2:将sOldWord 替换成罕见字符串:@#$(" + sOldWord+ ")$#@26 for (int i = 0; i < listReplaceRule.size(); i++) {27 sOldWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("oldword"));28 sNewWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("newword"));29 sRule = uFunc.getString(((Map) listReplaceRule.get(i)).get("rule"));30 if ("1".equals(sRule)) {31 sOldString = sOldString.replace(sOldWord, "@#$(" + sOldWord+ ")$#@");32 }33 }34 //Step 2:将罕见字符串:@#$(" + sOldWord+ ")$#@ 替换成 sNewWord35 for (int i = 0; i < listReplaceRule.size(); i++) {36 sOldWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("oldword"));37 sNewWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("newword"));38 sRule = uFunc.getString(((Map) listReplaceRule.get(i)).get("rule"));39 if ("1".equals(sRule)) {40 sOldString = sOldString.replace("@#$(" + sOldWord + ")$#@",sNewWord);41 }42 }43 return sOldString;44 45 }
解决方式二:字符位置替换法
主要思路:记录全部需要替换的字符在源字符串OldString中的Index在List:listIndex中,循环源OldString,如果存在于listIndex中,则将index位置上的oldchar替换为newchar;
不存在则取源OldString中index位置的值。PS: 字符互换相当于2次的字符替换
Step 1:将rule1和rule2分拆为2个ArrayList
1 //将规则1和规则2分拆为2个ArrayList 2 ArrayList listRule1= (ArrayList)listReplaceRule.clone(); 3 ArrayList listRule2= new ArrayList(); 4 ArrayList alRule1Word=new ArrayList(); 5 ArrayList alRule2Word=new ArrayList(); 6 for (int i = 0; i < listReplaceRule.size(); i++) { 7 String sOldWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("oldword")); 8 String sNewWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("newword")); 9 String sRule = uFunc.getString(((Map) listReplaceRule.get(i)).get("rule"));10 if ("2".equals(sRule) && sOldString.indexOf(sOldWord) >= 0 && sOldString.indexOf(sNewWord) >= 0) {11 listRule1.remove(listReplaceRule.get(i));12 Map obj=new HashMap();13 obj.put("oldword",sOldWord);14 obj.put("newword",sNewWord);15 obj.put("rule","1");16 listRule2.add(0,obj);17 Map obj1=new HashMap();18 obj1.put("oldword",sNewWord);19 obj1.put("newword",sOldWord);20 obj1.put("rule","1");21 listRule2.add(0,obj1);22 23 alRule2Word.add(sOldWord);24 alRule2Word.add(sNewWord);25 }26 }
Step 2:检查二义性
1 for (Object object : listRule1) {2 String sOldWord = uFunc.getString(((Map)object).get("oldword"));3 alRule1Word.add(sOldWord);4 }5 Set setRule1Word = new HashSet(alRule1Word);6 Set setRule2Word = new HashSet(alRule2Word);7 if(alRule1Word.size()!=setRule1Word.size()|| alRule2Word.size()!=setRule2Word.size())8 return null;
Step 3:先处理规则2,将需要互换的字符进行互换位置
1 SortedMap mStringIndex2 = getIndexMap(sOldString, listRule2); //返回要替换字符的位置SortedMap 2 for(int i = 0; i < sOldString.length(); i++) 3 { 4 if(mStringIndex2.containsKey(i)){ 5 sBuilder=sBuilder.append(uFunc.getString(mStringIndex2.get(i))); 6 } 7 else { 8 sBuilder=sBuilder.append(sOldString.substring(i,i+1)); 9 }10 }
Step 4:处理规则2后的字符串sBuilder赋值给源字符串sOldString
1 sOldString=sBuilder.toString(); 2 sBuilder.delete(0, sBuilder.length());//清空sBuilder
Step 5:再处理规则1,把旧字符替换成新字符
1 SortedMap mStringIndex1 = getIndexMap(sOldString, listRule1); //返回要替换字符的位置SortedMap 2 for(int i = 0; i < sOldString.length(); i++) 3 { 4 if(mStringIndex1.containsKey(i)){ 5 int start=Integer.parseInt(((Map)mStringIndex1.get(i)).get("start").toString()); 6 int lenghth=Integer.parseInt(((Map)mStringIndex1.get(i)).get("length").toString()); 7 String word=((Map)mStringIndex1.get(i)).get("word").toString(); 8 sBuilder=sBuilder.append(word); 9 i=i+lenghth-1;10 }11 else {12 sBuilder=sBuilder.append(sOldString.substring(i,i+1));13 }14 }
Step 6: 返回结果字符串
1 return sBuilder.toString();
完整代码:
1 */ 2 /** 3 * 替换字符串 4 * @param sOldString 需要替换的源字符串 5 * @param listReplaceRule 替换规则 6 * @param sOldWord 要替换的字符串 7 * @param sNewWord 替换后的字符串 8 * @throws IOException 9 */ 10 public static String replaceSring2(String sOldString,ArrayList listReplaceRule) throws IOException { 11 12 StringBuilder sBuilder=new StringBuilder(); 13 //Step 1:将规则1和规则2分拆为2个ArrayList 14 ArrayList listRule1= (ArrayList)listReplaceRule.clone(); 15 ArrayList listRule2= new ArrayList(); 16 ArrayList alRule1Word=new ArrayList(); 17 ArrayList alRule2Word=new ArrayList(); 18 for (int i = 0; i < listReplaceRule.size(); i++) { 19 String sOldWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("oldword")); 20 String sNewWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("newword")); 21 String sRule = uFunc.getString(((Map) listReplaceRule.get(i)).get("rule")); 22 if ("2".equals(sRule) && sOldString.indexOf(sOldWord) >= 0 && sOldString.indexOf(sNewWord) >= 0) { 23 listRule1.remove(listReplaceRule.get(i)); 24 Map obj=new HashMap(); 25 obj.put("oldword",sOldWord); 26 obj.put("newword",sNewWord); 27 obj.put("rule","1"); 28 listRule2.add(0,obj); 29 Map obj1=new HashMap(); 30 obj1.put("oldword",sNewWord); 31 obj1.put("newword",sOldWord); 32 obj1.put("rule","1"); 33 listRule2.add(0,obj1); 34 35 alRule2Word.add(sOldWord); 36 alRule2Word.add(sNewWord); 37 } 38 } 39 40 //Step 2:检查二义性 41 for (Object object : listRule1) { 42 String sOldWord = uFunc.getString(((Map)object).get("oldword")); 43 alRule1Word.add(sOldWord); 44 } 45 Set setRule1Word = new HashSet(alRule1Word); 46 Set setRule2Word = new HashSet(alRule2Word); 47 if(alRule1Word.size()!=setRule1Word.size()|| alRule2Word.size()!=setRule2Word.size()) 48 return null; 49 //Step 3:先处理规则2,将需要互换的字符进行互换位置 50 SortedMap mStringIndex2 = getIndexMap(sOldString, listRule2); //返回要替换字符的位置SortedMap 51 for(int i = 0; i < sOldString.length(); i++) 52 { 53 if(mStringIndex2.containsKey(i)){ 54 sBuilder=sBuilder.append(uFunc.getString(mStringIndex2.get(i))); 55 } 56 else { 57 sBuilder=sBuilder.append(sOldString.substring(i,i+1)); 58 } 59 } 60 //Step 4:处理规则2后的字符串sBuilder赋值给源字符串sOldString 61 sOldString=sBuilder.toString(); 62 sBuilder.delete(0, sBuilder.length());//清空sBuilder 63 //Step 5:再处理规则1,把旧字符替换成新字符 64 SortedMap mStringIndex1 = getIndexMap(sOldString, listRule1); //返回要替换字符的位置SortedMap 65 for(int i = 0; i < sOldString.length(); i++) 66 { 67 if(mStringIndex1.containsKey(i)){ 68 int start=Integer.parseInt(((Map)mStringIndex1.get(i)).get("start").toString()); 69 int lenghth=Integer.parseInt(((Map)mStringIndex1.get(i)).get("length").toString()); 70 String word=((Map)mStringIndex1.get(i)).get("word").toString(); 71 sBuilder=sBuilder.append(word); 72 i=i+lenghth-1; 73 } 74 else { 75 sBuilder=sBuilder.append(sOldString.substring(i,i+1)); 76 } 77 } 78 //返回结果字符串 79 return sBuilder.toString(); 80 } 81 private static SortedMap getIndexMap(String sOldString,ArrayList listReplaceRule) throws IOException 82 { 83 SortedMap tmpMap = new TreeMap(); 84 for (int i = 0; i < listReplaceRule.size(); i++) { 85 String sOldWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("oldword")); 86 String sNewWord = uFunc.getString(((Map) listReplaceRule.get(i)).get("newword")); 87 //String sRid = uFunc.getString(((Map) listReplaceRule.get(i)).get("rid")); 88 if(sOldString.indexOf(sOldWord)>=0){ 89 for (int j = 0; j < sOldString.length(); j++) { 90 int index = sOldString.indexOf(sOldWord, j); 91 if (index < 0) { 92 break; 93 } 94 Map map = new HashMap(); 95 map.put("start", sOldString.indexOf(sOldWord)); 96 map.put("length", sOldWord.length()); 97 map.put("word", sNewWord); 98 tmpMap.put(index, map); 99 j = index;100 }101 }102 }103 return tmpMap;104 }