如何在.NET中通过索引有效地覆盖部分字符串?

时间:2010-08-11 14:15:45

标签: .net string stringbuilder

在我的.NET程序中,我允许用户定义“字段”,这些字段是业务逻辑计算的值。这些字段具有位置和长度,因此它们都可以插入到给定索引处的单个输出字符串中。我还允许用户指定此输出字符串的默认内容。如果没有定义字段来替换给定位置,则输出默认字符

我的问题是,我该如何有效地做到这一点? StringBuilder类有一个 Insert(int index,string value)方法,但每次都会延长输出字符串而不是覆盖它。我是否必须使用 StringBuilder [int index] 索引器一次设置一个char,这是效率低吗?由于我将要这么做很多次,我希望它尽可能快。

感谢。

7 个答案:

答案 0 :(得分:3)

一次做一个角色可能是你最好的选择。我这样说是因为在Insert上调用RemoveStringBuilder会导致字符向右/向左移动,就像任何可变索引集合中的类似方法一样,例如{{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/

http://philosopherdeveloper.wordpress.com/2010/06/13/string-manipulation-in-net-epilogue-plus-new-theme/

答案 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 ) );
    }