替换子字符串的优雅解决方案
Elegant solution to replace substrings
我遇到了一个具有挑战性的问题。我有一个未修改的字符串,例如 abcdefg
和一个包含字符串和索引的对象数组。
例如,对象 1 包含 d
和索引 [1, 2]
;
然后我会用 d
替换子字符串 [1,2]
中的任何字母,结果字符串看起来像 adcdefg
.
当替换文本的长度与被替换文本的长度不同时,就会出现问题。我需要一些方法来跟踪长度变化,否则进一步替换的索引将不准确。
这是我目前的情况:
for (CandidateResult cResult : candidateResultList) {
int[] index = cResult.getIndex();
finalResult = finalResult.substring(0, index[0]) + cResult.getCandidate()
+ finalResult.substring(index[1], finalResult.length()); //should switch to stringbuilder
}
return finalResult;
这并没有解决上面提到的极端情况。
此外,如果有人想知道,这不是作业。这实际上是我正在创建的一个 ocr 培训师程序。
这是一个实现,我还没有测试过,但你可以试着了解一下。我会根据需要在代码中添加注释。
/** This class represents a replacement of characters in the original String, s[i0:if],
* with a new string, str.
**/
class Replacement{
int s, e;
String str;
public Replacement(int s, int e, String str){
this.s = s;
this.e = e;
this.str = str;
}
}
String stringReplace(String str, List<Replacement> replacements){
// Sort Replacements by starting index
Collections.sort(replacements, new Comparator<Replacement>(){
@Override public int compare(Replacement r1, Replacement r2){
return Integer.compare(r1.s, r2.s);
}
};
StringBuilder sb = new StringBuilder();
int repPos = 0;
for(int i = 0; i < str.length; i++){
Replacement rep = replacements.get(repPos);
if(rep.s == i){ // Replacement starts here, at i == s
sb.append(rep.str); // Append the replacement
i = rep.e - 1; // Advance i -> e - 1
repPos++; // Advance repPos by 1
} else {
sb.append(str.charAt(i)); // No replacement, append char
}
}
return sb.toString();
}
[Edit:] 看到ptyx的回答后,我觉得那种方式可能更优雅。如果你倒序排序,你应该不必担心不同的长度:
String stringReplace(String str, List<Replacement> replacements){
// Sort Replacements in reverse order by index
Collections.sort(replacements, new Comparator<Replacement>(){
@Override public int compare(Replacement r1, Replacement r2){
return -Integer.compare(r1.s, r2.s); // Note reverse order
}
};
// By replacing in reverse order, shouldn't affect next replacement.
StringBuilder sb = new StringBuilder(str);
for(Replacement rep : replacements){
sb.replace(rep.s, rep.e, rep.str);
}
return sb.toString();
}
假设没有要替换的重叠范围,以相反的位置顺序处理您的替换 - 完成。
用什么替换 [5-6] 并不重要,它永远不会修改 [0-4] 因此您不需要为任何索引映射而烦恼,例如:[1,2 ]
这似乎按照你的要求做了,基本上你只是根据以前的插入翻译替换
public static void main(String[] args) {
Replacer[] replacers = {
new Replacer(new int[]{ 1 , 2 }, "ddd") ,
new Replacer(new int[]{ 2 , 3 }, "a")
};
System.out.println(
m("abcdefg", replacers));
}
public static String m(String s1, Replacer[] replacers){
StringBuilder builder = new StringBuilder(s1);
int translate = 0;
for (int i = 0 ; i < replacers.length ; i++) {
translate += replacers[i].replace(builder, translate);
}
return builder.toString();
}
public static class Replacer{
int[] arr;
String toRep;
public Replacer(int[] arr, String toRep) {
this.arr = arr;
this.toRep = toRep;
}
public int replace(StringBuilder b, int translate){
b.replace(arr[0] + translate, arr[1] + translate, toRep);
return arr[1];
}
}
我遇到了一个具有挑战性的问题。我有一个未修改的字符串,例如 abcdefg
和一个包含字符串和索引的对象数组。
例如,对象 1 包含 d
和索引 [1, 2]
;
然后我会用 d
替换子字符串 [1,2]
中的任何字母,结果字符串看起来像 adcdefg
.
当替换文本的长度与被替换文本的长度不同时,就会出现问题。我需要一些方法来跟踪长度变化,否则进一步替换的索引将不准确。
这是我目前的情况:
for (CandidateResult cResult : candidateResultList) {
int[] index = cResult.getIndex();
finalResult = finalResult.substring(0, index[0]) + cResult.getCandidate()
+ finalResult.substring(index[1], finalResult.length()); //should switch to stringbuilder
}
return finalResult;
这并没有解决上面提到的极端情况。
此外,如果有人想知道,这不是作业。这实际上是我正在创建的一个 ocr 培训师程序。
这是一个实现,我还没有测试过,但你可以试着了解一下。我会根据需要在代码中添加注释。
/** This class represents a replacement of characters in the original String, s[i0:if],
* with a new string, str.
**/
class Replacement{
int s, e;
String str;
public Replacement(int s, int e, String str){
this.s = s;
this.e = e;
this.str = str;
}
}
String stringReplace(String str, List<Replacement> replacements){
// Sort Replacements by starting index
Collections.sort(replacements, new Comparator<Replacement>(){
@Override public int compare(Replacement r1, Replacement r2){
return Integer.compare(r1.s, r2.s);
}
};
StringBuilder sb = new StringBuilder();
int repPos = 0;
for(int i = 0; i < str.length; i++){
Replacement rep = replacements.get(repPos);
if(rep.s == i){ // Replacement starts here, at i == s
sb.append(rep.str); // Append the replacement
i = rep.e - 1; // Advance i -> e - 1
repPos++; // Advance repPos by 1
} else {
sb.append(str.charAt(i)); // No replacement, append char
}
}
return sb.toString();
}
[Edit:] 看到ptyx的回答后,我觉得那种方式可能更优雅。如果你倒序排序,你应该不必担心不同的长度:
String stringReplace(String str, List<Replacement> replacements){
// Sort Replacements in reverse order by index
Collections.sort(replacements, new Comparator<Replacement>(){
@Override public int compare(Replacement r1, Replacement r2){
return -Integer.compare(r1.s, r2.s); // Note reverse order
}
};
// By replacing in reverse order, shouldn't affect next replacement.
StringBuilder sb = new StringBuilder(str);
for(Replacement rep : replacements){
sb.replace(rep.s, rep.e, rep.str);
}
return sb.toString();
}
假设没有要替换的重叠范围,以相反的位置顺序处理您的替换 - 完成。
用什么替换 [5-6] 并不重要,它永远不会修改 [0-4] 因此您不需要为任何索引映射而烦恼,例如:[1,2 ]
这似乎按照你的要求做了,基本上你只是根据以前的插入翻译替换
public static void main(String[] args) {
Replacer[] replacers = {
new Replacer(new int[]{ 1 , 2 }, "ddd") ,
new Replacer(new int[]{ 2 , 3 }, "a")
};
System.out.println(
m("abcdefg", replacers));
}
public static String m(String s1, Replacer[] replacers){
StringBuilder builder = new StringBuilder(s1);
int translate = 0;
for (int i = 0 ; i < replacers.length ; i++) {
translate += replacers[i].replace(builder, translate);
}
return builder.toString();
}
public static class Replacer{
int[] arr;
String toRep;
public Replacer(int[] arr, String toRep) {
this.arr = arr;
this.toRep = toRep;
}
public int replace(StringBuilder b, int translate){
b.replace(arr[0] + translate, arr[1] + translate, toRep);
return arr[1];
}
}