多种形式字符串转换的最佳转换结构设计
需求是这样的:
对于这些格式: aaaAaa, AaaAaa, aaa_aaa, aaa_Aaa, Aaa_aaa, Aaa_Aaa, 写一个算法实现任意两种格式的字符串互相转换.
例如如下调用:
convert(“Abbbbb_Asdfdsf_Bwieru”, “Aaa_Aaa”, “aaa_Aaa”);
可以得到转换结果abbbbb_Asdfdsf_Bwieru.
函数签名为: convert(String inStr, String inPtn, String outPtn)
这个需求的任意两种格式之间的转换并不是很难, 问题是, 什么样的转换结构既容易实现, 又结构清晰, 且便于扩展呢?
本文目的不在于实现6种格式的字符串转换, 而是寻找一种最优的转换结构, 使得哪怕即使有100种格式的字符串也可以任意转换. 这种结构不仅仅可以用于字符串转换, 其他的比如数据转换, 单位转化等等合适的地方都可以使用这种转换结构.
如下图, 有3种转换结构:
- 第一种方法, 枚举法,
需要实现任意两种格式之间的转换算法, n种格式需要n×(n-1)/2种转换算法, 6种格式需要15种,
假如有20种格式则需要190种算法. 这种结构缺点很明显, 代码量太多, 算法太多, 容易出错. 且不容易扩展, 在已有20种格式的基础上再增加一种格式, 则需要再增加20种算法, 这种结构排除. - 第二种方法, 环状责任链.
由结构图看, 结构很清晰, 根据传入字符串的格式, 选择合适的入环点与出环点, 完成转换.
这种方式优于第一种方法, 实现起来很简单, 且易于扩展(每增加一种格式, 只需要增加两种转换算法). 但是也有个缺点, 那就是随着环的加长, 效率会越来越低. 如果环有100个元素, 有可能经过99次转换才得到需要的结果. - 第三种方法, 中介法
中介法可以解决环状责任链的缺点, 无论有多少种格式, 只需要最多2次转换即可得到结果. 且结构也比较简单, 易于扩展. 作为转换中介的格式尽量选择与其他格式容易转换的格式, 这样算法可以更简单.
下面以java分别实现这两种转换结构:
环状责任链
package test.stringconvert;
import java.util.Arrays;
public class TestStringConvertLoop {
public static void main(String[] args) {
testCovnvert("abbbbbGsdfdsfHwieru", "aaaAaa");
System.out.println();
testCovnvert("abbbbb_asdfdsf_bwieru", "aaa_aaa");
System.out.println();
testCovnvert("abbbbb_Asdfdsf_Bwieru", "aaa_Aaa");
System.out.println();
testCovnvert("Abbbbb_asdfdsf_bwieru", "Aaa_aaa");
System.out.println();
testCovnvert("Abbbbb_Asdfdsf_Bwieru", "Aaa_Aaa");
System.out.println();
testCovnvert("AbbbbbAsdfdsfBwieru", "AaaAaa");
}
/**
*
* @param inStr
* 输入字符串
* @param inPtn
* 输入字符串的格式
*/
private static void testCovnvert(String inStr, String inPtn) {
String[] outPtnArray = { "aaaAaa", "AaaAaa", "aaa_aaa", "aaa_Aaa", "Aaa_aaa", "Aaa_Aaa" };
for (String outPtn : outPtnArray) {
System.out.println(inPtn + " -> " + outPtn + " : " + inStr + " -> " + convert(inStr, inPtn, outPtn));
}
}
/**
* 环节点.
*
*/
static abstract class ConvertLoop {
/** 当前环节点格式. */
protected String thisPtn = "";
/** 下一个环节点. */
private ConvertLoop nextConvert;
ConvertLoop(String thisPtn) {
this.thisPtn = thisPtn;
}
/**
* 字符串转换.
*
* @param inStr
* 转换前字符串
* @return 转换后字符串
*/
protected abstract String convertStr(String inStr);
/**
* 选择入环点.
*
* @param inStr
* @param inPtn
* @param outPtn
* @return
*/
protected String enterLoop(String inStr, String inPtn, String outPtn) {
// 当前环节点格式为入环格式时入环, 否则判断下一个环节点.
if (inPtn.equals(thisPtn)) {
return convert(inStr, inPtn, outPtn);
} else {
return nextConvert.enterLoop(inStr, inPtn, outPtn);
}
}
/**
* 设置下一个环节点.
*
* @param nextConvert
*/
public void setNextConvert(ConvertLoop nextConvert) {
this.nextConvert = nextConvert;
}
/**
* 父类转换逻辑
*
* @param inStr
* @param inPtn
* @param outPtn
* @return
*/
private String convert(String inStr, String inPtn, String outPtn) {
// 若当前格式是输出格式, 直接返回.
if (outPtn.equals(thisPtn)) {
return inStr;
}
// 否则转换并进入下一个环节点
String convertStr = convertStr(inStr);
return nextConvert.convert(convertStr, inPtn, outPtn);
}
}
private static String convert(String inStr, String inPtn, String outPtn) {
// aaaAaa
// AaaAaa
// aaa_aaa
// aaa_Aaa
// Aaa_aaa
// Aaa_Aaa
ConvertLoop ptn_aaaAaa = new ConvertLoop("aaaAaa") {
@Override
protected String convertStr(String inStr) {
// aaaAaa -> AaaAaa
return Character.toUpperCase(inStr.charAt(0)) + inStr.substring(1);
}
};
ConvertLoop ptn_AaaAaa = new ConvertLoop("AaaAaa") {
@Override
protected String convertStr(String inStr) {
// AaaAaa -> aaa_aaa
String convertRes = Character.toLowerCase(inStr.charAt(0))
+ inStr.substring(1).replaceAll("([A-Z])", "_$1").toLowerCase();
return convertRes;
}
};
ConvertLoop ptn_aaa_aaa = new ConvertLoop("aaa_aaa") {
@Override
protected String convertStr(String inStr) {
// aaa_aaa -> aaa_Aaa
char[] srcArray = inStr.toCharArray();
char[] resArray = Arrays.copyOf(srcArray, srcArray.length);
for (int i = 0, len = srcArray.length; i < len; i++) {
if (srcArray[i] == '_') {
i++;
resArray[i] = Character.toUpperCase(srcArray[i]);
}
}
return new String(resArray);
}
};
ConvertLoop ptn_aaa_Aaa = new ConvertLoop("aaa_Aaa") {
@Override
protected String convertStr(String inStr) {
// aaa_Aaa -> Aaa_aaa
return Character.toUpperCase(inStr.charAt(0)) + inStr.substring(1).toLowerCase();
}
};
ConvertLoop ptn_Aaa_aaa = new ConvertLoop("Aaa_aaa") {
@Override
protected String convertStr(String inStr) {
// Aaa_aaa -> Aaa_Aaa
char[] srcArray = inStr.toCharArray();
char[] resArray = Arrays.copyOf(srcArray, srcArray.length);
for (int i = 0, len = srcArray.length; i < len; i++) {
if (srcArray[i] == '_') {
i++;
resArray[i] = Character.toUpperCase(srcArray[i]);
}
}
return new String(resArray);
}
};
ConvertLoop ptn_Aaa_Aaa = new ConvertLoop("Aaa_Aaa") {
@Override
protected String convertStr(String inStr) {
// Aaa_Aaa -> aaaAaa
return Character.toLowerCase(inStr.charAt(0)) + inStr.substring(1).replace("_", "");
}
};
// 构造环
ptn_aaaAaa.setNextConvert(ptn_AaaAaa);
ptn_AaaAaa.setNextConvert(ptn_aaa_aaa);
ptn_aaa_aaa.setNextConvert(ptn_aaa_Aaa);
ptn_aaa_Aaa.setNextConvert(ptn_Aaa_aaa);
ptn_Aaa_aaa.setNextConvert(ptn_Aaa_Aaa);
ptn_Aaa_Aaa.setNextConvert(ptn_aaaAaa);
return ptn_aaaAaa.enterLoop(inStr, inPtn, outPtn);
}
}
测试结果:
aaaAaa -> aaaAaa : abbbbbGsdfdsfHwieru -> abbbbbGsdfdsfHwieru
aaaAaa -> AaaAaa : abbbbbGsdfdsfHwieru -> AbbbbbGsdfdsfHwieru
aaaAaa -> aaa_aaa : abbbbbGsdfdsfHwieru -> abbbbb_gsdfdsf_hwieru
aaaAaa -> aaa_Aaa : abbbbbGsdfdsfHwieru -> abbbbb_Gsdfdsf_Hwieru
aaaAaa -> Aaa_aaa : abbbbbGsdfdsfHwieru -> Abbbbb_gsdfdsf_hwieru
aaaAaa -> Aaa_Aaa : abbbbbGsdfdsfHwieru -> Abbbbb_Gsdfdsf_Hwieru
aaa_aaa -> aaaAaa : abbbbb_asdfdsf_bwieru -> abbbbbAsdfdsfBwieru
aaa_aaa -> AaaAaa : abbbbb_asdfdsf_bwieru -> AbbbbbAsdfdsfBwieru
aaa_aaa -> aaa_aaa : abbbbb_asdfdsf_bwieru -> abbbbb_asdfdsf_bwieru
aaa_aaa -> aaa_Aaa : abbbbb_asdfdsf_bwieru -> abbbbb_Asdfdsf_Bwieru
aaa_aaa -> Aaa_aaa : abbbbb_asdfdsf_bwieru -> Abbbbb_asdfdsf_bwieru
aaa_aaa -> Aaa_Aaa : abbbbb_asdfdsf_bwieru -> Abbbbb_Asdfdsf_Bwieru
aaa_Aaa -> aaaAaa : abbbbb_Asdfdsf_Bwieru -> abbbbbAsdfdsfBwieru
aaa_Aaa -> AaaAaa : abbbbb_Asdfdsf_Bwieru -> AbbbbbAsdfdsfBwieru
aaa_Aaa -> aaa_aaa : abbbbb_Asdfdsf_Bwieru -> abbbbb_asdfdsf_bwieru
aaa_Aaa -> aaa_Aaa : abbbbb_Asdfdsf_Bwieru -> abbbbb_Asdfdsf_Bwieru
aaa_Aaa -> Aaa_aaa : abbbbb_Asdfdsf_Bwieru -> Abbbbb_asdfdsf_bwieru
aaa_Aaa -> Aaa_Aaa : abbbbb_Asdfdsf_Bwieru -> Abbbbb_Asdfdsf_Bwieru
Aaa_aaa -> aaaAaa : Abbbbb_asdfdsf_bwieru -> abbbbbAsdfdsfBwieru
Aaa_aaa -> AaaAaa : Abbbbb_asdfdsf_bwieru -> AbbbbbAsdfdsfBwieru
Aaa_aaa -> aaa_aaa : Abbbbb_asdfdsf_bwieru -> abbbbb_asdfdsf_bwieru
Aaa_aaa -> aaa_Aaa : Abbbbb_asdfdsf_bwieru -> abbbbb_Asdfdsf_Bwieru
Aaa_aaa -> Aaa_aaa : Abbbbb_asdfdsf_bwieru -> Abbbbb_asdfdsf_bwieru
Aaa_aaa -> Aaa_Aaa : Abbbbb_asdfdsf_bwieru -> Abbbbb_Asdfdsf_Bwieru
Aaa_Aaa -> aaaAaa : Abbbbb_Asdfdsf_Bwieru -> abbbbbAsdfdsfBwieru
Aaa_Aaa -> AaaAaa : Abbbbb_Asdfdsf_Bwieru -> AbbbbbAsdfdsfBwieru
Aaa_Aaa -> aaa_aaa : Abbbbb_Asdfdsf_Bwieru -> abbbbb_asdfdsf_bwieru
Aaa_Aaa -> aaa_Aaa : Abbbbb_Asdfdsf_Bwieru -> abbbbb_Asdfdsf_Bwieru
Aaa_Aaa -> Aaa_aaa : Abbbbb_Asdfdsf_Bwieru -> Abbbbb_asdfdsf_bwieru
Aaa_Aaa -> Aaa_Aaa : Abbbbb_Asdfdsf_Bwieru -> Abbbbb_Asdfdsf_Bwieru
AaaAaa -> aaaAaa : AbbbbbAsdfdsfBwieru -> abbbbbAsdfdsfBwieru
AaaAaa -> AaaAaa : AbbbbbAsdfdsfBwieru -> AbbbbbAsdfdsfBwieru
AaaAaa -> aaa_aaa : AbbbbbAsdfdsfBwieru -> abbbbb_asdfdsf_bwieru
AaaAaa -> aaa_Aaa : AbbbbbAsdfdsfBwieru -> abbbbb_Asdfdsf_Bwieru
AaaAaa -> Aaa_aaa : AbbbbbAsdfdsfBwieru -> Abbbbb_asdfdsf_bwieru
AaaAaa -> Aaa_Aaa : AbbbbbAsdfdsfBwieru -> Abbbbb_Asdfdsf_Bwieru
中介法
package test.stringconvert;
import java.util.HashMap;
import java.util.Map;
public class TestStringConvertMedia {
public static void main(String[] args) {
testCovnvert("abbbbbGsdfdsfHwieru", "aaaAaa");
System.out.println();
testCovnvert("abbbbb_asdfdsf_bwieru", "aaa_aaa");
System.out.println();
testCovnvert("abbbbb_Asdfdsf_Bwieru", "aaa_Aaa");
System.out.println();
testCovnvert("Abbbbb_asdfdsf_bwieru", "Aaa_aaa");
System.out.println();
testCovnvert("Abbbbb_Asdfdsf_Bwieru", "Aaa_Aaa");
System.out.println();
testCovnvert("AbbbbbAsdfdsfBwieru", "AaaAaa");
}
/**
* 测试函数.
*
* @param inStr
* @param inPtn
*/
private static void testCovnvert(String inStr, String inPtn) {
String[] outPtnArray = { "aaaAaa", "AaaAaa", "aaa_aaa", "aaa_Aaa", "Aaa_aaa", "Aaa_Aaa" };
for (String outPtn : outPtnArray) {
System.out.println(inPtn + " -> " + outPtn + " : " + inStr + " -> " + convert(inStr, inPtn, outPtn));
}
}
/**
* 转换逻辑.
*
* @param inStr
* 转换前字符串
* @param inPtn
* 转换前格式
* @param outPtn
* 转换后格式
* @return 转换后字符串
*/
private static String convert(String inStr, String inPtn, String outPtn) {
// 构造转换结构
Map<String, ConvertNode> convertMap = getConvertMap();
String mediaStr = inStr;
// 如果不是中介格式, 则从map中获取转换函数, 转换为中介格式.
if (!inPtn.equals("AaaAaa")) {
mediaStr = convertMap.get(inPtn).toMedia(inStr);
}
// 若目标格式是中介格式, 则直接返回.
if (outPtn.equals("AaaAaa")) {
return mediaStr;
}
// 否则, 获取 中介 -> 目标格式 的转换算法, 获取转换结果.
String resStr = convertMap.get(outPtn).fromMedia(mediaStr);
return resStr;
}
/**
* 转换节点.
*
*
*/
static abstract class ConvertNode {
protected String thisPtn = "";
ConvertNode(String thisPtn) {
this.thisPtn = thisPtn;
}
/**
* 转换该格式为中介格式.
*
* @param inStr
* @return
*/
protected abstract String toMedia(String inStr);
/**
* 中介格式转换为该格式.
*
* @param inStr
* @return
*/
protected abstract String fromMedia(String inStr);
}
private static Map<String, ConvertNode> getConvertMap() {
Map<String, ConvertNode> convertMap = new HashMap<>();
// 构造节点
// 中介格式是 :AaaAaa
// 以下为叶子节点:
// aaaAaa
// aaa_aaa
// aaa_Aaa
// Aaa_aaa
// Aaa_Aaa
ConvertNode ptn_aaaAaa = new ConvertNode("aaaAaa") {
@Override
protected String toMedia(String inStr) {
// aaaAaa -> AaaAaa
return Character.toUpperCase(inStr.charAt(0)) + inStr.substring(1);
}
@Override
protected String fromMedia(String inStr) {
// AaaAaa -> aaaAaa
return Character.toLowerCase(inStr.charAt(0)) + inStr.substring(1);
}
};
ConvertNode ptn_aaa_aaa = new ConvertNode("aaa_aaa") {
@Override
protected String toMedia(String inStr) {
// aaa_aaa -> AaaAaa
char[] srcArray = inStr.toCharArray();
char[] resArray = new char[inStr.replace("_", "").length()];
resArray[0] = Character.toUpperCase(srcArray[0]);
for (int i = 1, j = 1, len = srcArray.length; i < len; i++, j++) {
resArray[j] = srcArray[i];
if (srcArray[i] == '_') {
i++;
resArray[j] = Character.toUpperCase(srcArray[i]);
}
}
return new String(resArray);
}
@Override
protected String fromMedia(String inStr) {
// AaaAaa -> aaa_aaa
return (inStr.charAt(0) + inStr.substring(1).replaceAll("([A-Z])", "_$1")).toLowerCase();
}
};
ConvertNode ptn_aaa_Aaa = new ConvertNode("aaa_Aaa") {
@Override
protected String toMedia(String inStr) {
// aaa_Aaa -> AaaAaa
String replace = inStr.replace("_", "");
return Character.toUpperCase(replace.charAt(0)) + replace.substring(1);
}
@Override
protected String fromMedia(String inStr) {
// AaaAaa -> aaa_Aaa
return Character.toLowerCase(inStr.charAt(0)) + inStr.substring(1).replaceAll("([A-Z])", "_$1");
}
};
ConvertNode ptn_Aaa_aaa = new ConvertNode("Aaa_aaa") {
@Override
protected String toMedia(String inStr) {
// Aaa_aaa -> AaaAaa
char[] srcArray = inStr.toCharArray();
char[] resArray = new char[inStr.replace("_", "").length()];
for (int i = 0, j = 0, len = srcArray.length; i < len; i++, j++) {
resArray[j] = srcArray[i];
if (srcArray[i] == '_') {
i++;
resArray[j] = Character.toUpperCase(srcArray[i]);
}
}
return new String(resArray);
}
@Override
protected String fromMedia(String inStr) {
// AaaAaa -> Aaa_aaa
char[] srcArray = inStr.toCharArray();
char[] resArray = new char[inStr.replaceAll("([A-Z])", "_$1").length() - 1];
for (int i = 0, j = 0, len = srcArray.length; i < len; i++, j++) {
resArray[j] = srcArray[i];
if (i != 0 && Character.isUpperCase(srcArray[i])) {
resArray[j++] = '_';
resArray[j] = Character.toLowerCase(srcArray[i]);
}
}
return new String(resArray);
}
};
ConvertNode ptn_Aaa_Aaa = new ConvertNode("Aaa_Aaa") {
@Override
protected String toMedia(String inStr) {
// Aaa_Aaa -> AaaAaa
return inStr.replace("_", "");
}
@Override
protected String fromMedia(String inStr) {
// AaaAaa -> Aaa_Aaa
return inStr.charAt(0) + inStr.substring(1).replaceAll("([A-Z])", "_$1");
}
};
// 节点联网
convertMap.put("aaaAaa", ptn_aaaAaa);
convertMap.put("aaa_aaa", ptn_aaa_aaa);
convertMap.put("aaa_Aaa", ptn_aaa_Aaa);
convertMap.put("Aaa_aaa", ptn_Aaa_aaa);
convertMap.put("Aaa_Aaa", ptn_Aaa_Aaa);
return convertMap;
}
}
执行结果:
aaaAaa -> aaaAaa : abbbbbGsdfdsfHwieru -> abbbbbGsdfdsfHwieru
aaaAaa -> AaaAaa : abbbbbGsdfdsfHwieru -> AbbbbbGsdfdsfHwieru
aaaAaa -> aaa_aaa : abbbbbGsdfdsfHwieru -> abbbbb_gsdfdsf_hwieru
aaaAaa -> aaa_Aaa : abbbbbGsdfdsfHwieru -> abbbbb_Gsdfdsf_Hwieru
aaaAaa -> Aaa_aaa : abbbbbGsdfdsfHwieru -> Abbbbb_gsdfdsf_hwieru
aaaAaa -> Aaa_Aaa : abbbbbGsdfdsfHwieru -> Abbbbb_Gsdfdsf_Hwieru
aaa_aaa -> aaaAaa : abbbbb_asdfdsf_bwieru -> abbbbbAsdfdsfBwieru
aaa_aaa -> AaaAaa : abbbbb_asdfdsf_bwieru -> AbbbbbAsdfdsfBwieru
aaa_aaa -> aaa_aaa : abbbbb_asdfdsf_bwieru -> abbbbb_asdfdsf_bwieru
aaa_aaa -> aaa_Aaa : abbbbb_asdfdsf_bwieru -> abbbbb_Asdfdsf_Bwieru
aaa_aaa -> Aaa_aaa : abbbbb_asdfdsf_bwieru -> Abbbbb_asdfdsf_bwieru
aaa_aaa -> Aaa_Aaa : abbbbb_asdfdsf_bwieru -> Abbbbb_Asdfdsf_Bwieru
aaa_Aaa -> aaaAaa : abbbbb_Asdfdsf_Bwieru -> abbbbbAsdfdsfBwieru
aaa_Aaa -> AaaAaa : abbbbb_Asdfdsf_Bwieru -> AbbbbbAsdfdsfBwieru
aaa_Aaa -> aaa_aaa : abbbbb_Asdfdsf_Bwieru -> abbbbb_asdfdsf_bwieru
aaa_Aaa -> aaa_Aaa : abbbbb_Asdfdsf_Bwieru -> abbbbb_Asdfdsf_Bwieru
aaa_Aaa -> Aaa_aaa : abbbbb_Asdfdsf_Bwieru -> Abbbbb_asdfdsf_bwieru
aaa_Aaa -> Aaa_Aaa : abbbbb_Asdfdsf_Bwieru -> Abbbbb_Asdfdsf_Bwieru
Aaa_aaa -> aaaAaa : Abbbbb_asdfdsf_bwieru -> abbbbbAsdfdsfBwieru
Aaa_aaa -> AaaAaa : Abbbbb_asdfdsf_bwieru -> AbbbbbAsdfdsfBwieru
Aaa_aaa -> aaa_aaa : Abbbbb_asdfdsf_bwieru -> abbbbb_asdfdsf_bwieru
Aaa_aaa -> aaa_Aaa : Abbbbb_asdfdsf_bwieru -> abbbbb_Asdfdsf_Bwieru
Aaa_aaa -> Aaa_aaa : Abbbbb_asdfdsf_bwieru -> Abbbbb_asdfdsf_bwieru
Aaa_aaa -> Aaa_Aaa : Abbbbb_asdfdsf_bwieru -> Abbbbb_Asdfdsf_Bwieru
Aaa_Aaa -> aaaAaa : Abbbbb_Asdfdsf_Bwieru -> abbbbbAsdfdsfBwieru
Aaa_Aaa -> AaaAaa : Abbbbb_Asdfdsf_Bwieru -> AbbbbbAsdfdsfBwieru
Aaa_Aaa -> aaa_aaa : Abbbbb_Asdfdsf_Bwieru -> abbbbb_asdfdsf_bwieru
Aaa_Aaa -> aaa_Aaa : Abbbbb_Asdfdsf_Bwieru -> abbbbb_Asdfdsf_Bwieru
Aaa_Aaa -> Aaa_aaa : Abbbbb_Asdfdsf_Bwieru -> Abbbbb_asdfdsf_bwieru
Aaa_Aaa -> Aaa_Aaa : Abbbbb_Asdfdsf_Bwieru -> Abbbbb_Asdfdsf_Bwieru
AaaAaa -> aaaAaa : AbbbbbAsdfdsfBwieru -> abbbbbAsdfdsfBwieru
AaaAaa -> AaaAaa : AbbbbbAsdfdsfBwieru -> AbbbbbAsdfdsfBwieru
AaaAaa -> aaa_aaa : AbbbbbAsdfdsfBwieru -> abbbbb_asdfdsf_bwieru
AaaAaa -> aaa_Aaa : AbbbbbAsdfdsfBwieru -> abbbbb_Asdfdsf_Bwieru
AaaAaa -> Aaa_aaa : AbbbbbAsdfdsfBwieru -> Abbbbb_asdfdsf_bwieru
AaaAaa -> Aaa_Aaa : AbbbbbAsdfdsfBwieru -> Abbbbb_Asdfdsf_Bwieru
另外还有其他的结构, 比如上面的环,可以改为双向的环, 可以同时顺时针和逆时针旋转, 可以一定程度解决效率低的问题, 但是结构会更复杂.
中介法和json很类似, 我们用json格式作为数据交换, 数据A -> json数据 -> 数据B, 就是这个结构, 很容易理解.
若有更好的转换结构, 欢迎一起探讨.