给定一个字符串,我需要根据给定的映射替换子字符串。映射确定替换的开始位置,要替换的文本长度和替换字符串。映射根据以下方案:
public struct mapItem
{
public int offset;
public int length;
public string newString;
}
例如:给定映射{{0,3,"frog"},{9,3,"kva"}}
和字符串
"dog says gav"
我们从位置0开始将长度为3的子串替换为“青蛙”,即
dog - > frog
并从位置9开始长度为3的子串到“kva”,即
gav->kva
新字符串变为:
"frog says kva"
我怎样才能有效地做到这一点?
答案 0 :(得分:2)
您必须注意替换考虑到之前替换产生的转变。使用StringBuilder
也更有效,因为不会像string
操作那样在每个操作中分配新内存。 (字符串是不变的,这意味着在每个字符串操作中都会创建一个全新的字符串。)
var maps = new List<MapItem> { ... };
var sb = new StringBuilder("dog says gav");
int shift = 0;
foreach (MapItem map in maps.OrderBy(m => m.Offset)) {
sb.Remove(map.Offset + shift, map.Length);
sb.Insert(map.Offset + shift, map.NewString);
shift += map.NewString.Length - map.Length;
}
string result = sb.ToString();
OrderBy
确保替换从左到右执行。如果您知道按此顺序提供了映射,则可以删除OrderBy
。
另一种更简单的方法是从右端的替换开始并向后工作,这样角色转换不会改变尚未执行的替换的位置:
var sb = new StringBuilder("dog says gav");
foreach (MapItem map in maps.OrderByDescending(m => m.Offset)) {
sb.Remove(map.Offset, map.Length);
sb.Insert(map.Offset, map.NewString);
}
string result = sb.ToString();
如果映射已按升序排序,则简单的反向for
语句似乎是合适的:
var sb = new StringBuilder("dog says gav");
for (int i = maps.Count - 1; i >= 0; i--) {
MapItem map = maps[i];
sb.Remove(map.Offset, map.Length);
sb.Insert(map.Offset, map.NewString);
}
string result = sb.ToString();
答案 1 :(得分:0)
您可以在下面写一个Extension Method
:
public static class ExtensionMethod
{
public static string ReplaceSubstringByMap(this string str, List<mapItem> map)
{
int offsetShift = 0;
foreach (mapItem mapItem in map.OrderBy(x => x.offset))
{
str = str.Remove(mapItem.offset + offsetShift, mapItem.length).Insert(mapItem.offset + offsetShift, mapItem.newString);
offsetShift += mapItem.newString.Length - mapItem.length;
}
return str;
}
}
并调用它如下:
var map = new List<mapItem>
{
new mapItem
{
offset = 0,
length = 1,
newString = "frog"
},
new mapItem
{
offset = 9,
length = 1,
newString = "kva"
}
};
string str = "dog says gav";
var result = str.ReplaceSubstringByMap(map);