在我的.NET程序中,我允许用户定义“字段”,这些字段是业务逻辑计算的值。这些字段具有位置和长度,因此它们都可以插入到给定索引处的单个输出字符串中。我还允许用户指定此输出字符串的默认内容。如果没有定义字段来替换给定位置,则输出默认字符
我的问题是,我该如何有效地做到这一点? StringBuilder类有一个 Insert(int index,string value)方法,但每次都会延长输出字符串而不是覆盖它。我是否必须使用 StringBuilder [int index] 索引器一次设置一个char,这是效率低吗?由于我将要这么做很多次,我希望它尽可能快。
感谢。
答案 0 :(得分:3)
一次做一个角色可能是你最好的选择。我这样说是因为在Insert
上调用Remove
和StringBuilder
会导致字符向右/向左移动,就像任何可变索引集合中的类似方法一样,例如{{1} }。
也就是说,这是一种扩展方法的绝佳选择,可以让您的生活更轻松。
List<char>
用法示例:
public static StringBuilder ReplaceSubstring(this StringBuilder stringBuilder, int index, string replacement)
{
if (index + replacement.Length > stringBuilder.Length)
{
// You could throw an exception here, or you could just
// append to the end of the StringBuilder -- up to you.
throw new ArgumentOutOfRangeException();
}
for (int i = 0; i < replacement.Length; ++i)
{
stringBuilder[index + i] = replacement[i];
}
return stringBuilder;
}
输出:
My name is Bob.
答案 1 :(得分:2)
StringBuilder
类允许您构建可变字符串。在执行Remove
之前,请尝试使用Insert
函数。由于它随机可访问,它应该非常快。只要StringBuilder
保持相同的容量,就不会花时间在内存中复制字符串。如果您知道字符串会变长,请尝试在调用New StringBuilder()
答案 2 :(得分:1)
只要字符串是不可靠的,每次操作都会导致GC加载,甚至StringBuilder插入/删除调用。 我会通过插入点剪切源字符串,然后用需要插入的数据“压缩”它。 之后,您可以在列表中连接字符串,以获得结果字符串。
以下是执行拆分/压缩操作的示例代码。 它假设,字段被定义为(位置,长度,值)的外观。
public class Field
{
public int pos { get; set; }
public int len { get; set; }
public string value { get; set; }
public string tag { get; set; }
}
class Program
{
static void Main(string[] args)
{
var source = "You'r order price [price] and qty [qty].";
var fields = new List<Field>();
fields.Add(new Field()
{
pos = 18,
len = 7,
value = "15.99$",
tag = "price"
});
fields.Add(new Field()
{
pos = 37-3,
len = 5,
value = "7",
tag = "qty"
});
Console.WriteLine(Zip(Split(source, fields), fields));
Console.WriteLine(ReplaceRegex(source, fields));
}
static IEnumerable<string> Split(string source, IEnumerable<Field> fields)
{
var index = 0;
foreach (var field in fields.OrderBy(q => q.pos))
{
yield return source.Substring(index, field.pos - index);
index = field.pos + field.len;
}
yield return source.Substring(index, source.Length - index);
}
static string Zip(IEnumerable<string> splitted, IEnumerable<Field> fields)
{
var items = splitted.Zip(fields, (l, r) => new string[] { l, r.value }).SelectMany(q => q).ToList();
items.Add(splitted.Last());
return string.Concat(items);
}
static string ReplaceRegex(string source, IEnumerable<Field> fields)
{
var fieldsDict = fields.ToDictionary(q => q.tag);
var re = new Regex(@"\[(\w+)\]");
return re.Replace(source, new MatchEvaluator((m) => fieldsDict[m.Groups[1].Value].value));
}
}
顺便说一句,最好用正则表达式替换特殊用户标记,如[price],[qty]?
答案 3 :(得分:0)
我建议使用StringBuilder
课程。但是你可以用字符串来做,但可能有副作用。以下是一些博客文章,介绍如何操纵字符串和可能的副作用。
http://philosopherdeveloper.wordpress.com/2010/05/28/are-strings-really-immutable-in-net/
答案 4 :(得分:0)
如果您的字符串已经预编码了长度,则StringBuilder类具有
public StringBuilder Replace(string oldValue, string newValue, int startIndex, int count)
只需设置起始索引和count = 1即可替换该特定实例。
您可以做的另一件事是使用String.Format()。将所有预定义字段转换为索引,以便获得类似“此{0}非常{1}”的字符串,然后将参数与特定索引匹配并执行String.Format(myString,myParams);
-Raul
答案 5 :(得分:0)
如果替换子串将成为一个很大的瓶颈,你可能想要完全放弃子串。相反,将数据分解为可以独立修改的字符串。如下所示:
class DataLine
{
public string Field1;
public string Field2;
public string Field3;
public string OutputDataLine()
{
return Field1 + Field2 + Field3;
}
}
这是一个简单的静态示例,但我确信可以使其更通用,这样如果每个用户都以不同的方式定义字段,您就可以处理它。将数据分成字段后,如果仍需要修改字段中的单个字符,则至少不会触及整个数据集。
现在,这可能会将瓶颈推到OutputDataLine函数,具体取决于您对数据的处理方式。但如果有必要,可以单独处理。
答案 6 :(得分:0)
正如您所说,StringBuilder具有Insert方法但没有Overwrite方法。
所以我为我的项目创建了Overwrite扩展方法,见下文。
请注意,如果StringBuilder没有足够的空间,它将剪切值。但是,您可以轻松修改它的逻辑。
public static void Overwrite( this StringBuilder sb, int index, string value )
{
int len = Math.Min( value.Length, sb.Length - index );
sb.Remove( index, len );
sb.Insert( index, value.Substring( 0, len ) );
}