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

如何在.NET中通过索引有效地覆盖字符串的某些部分?,.net,string,stringbuilder,.net,String,Stringbuilder,在我的.NET程序中,我允许用户定义“字段”,这些字段是由业务逻辑计算的值。这些字段具有位置和长度,因此它们都可以插入到给定索引处的单个输出字符串中。我还允许用户指定此输出字符串的默认内容。如果未定义任何字段来替换给定位置,则输出默认字符 我的问题是,我如何才能有效地做到这一点?StringBuilder类有一个插入(int-index,string-value)方法,但每次都会延长输出字符串,而不是覆盖它。我是否必须使用StringBuilder[int index]索引器一次设置一个字符,这

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

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


谢谢。

只要字符串是不可变的,每次使用它进行操作都会导致GC加载,甚至是StringBuilder的插入/删除调用。 我将通过插入点剪切源字符串,然后用需要插入的数据“压缩”它。 之后,您可以只在列表中连接字符串,以获得结果字符串

下面是执行拆分/压缩操作的示例代码。 它假定字段定义为(位置、长度、值)的个数

公共类字段
{
公共int pos{get;set;}
公共整数len{get;set;}
公共字符串值{get;set;}
公共字符串标记{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
var source=“您的订单价格[价格]和数量[数量]”;
变量字段=新列表();
fields.Add(新字段()
{
位置=18,
len=7,
value=“15.99$”,
tag=“价格”
});
fields.Add(新字段()
{
位置=37-3,
len=5,
value=“7”,
tag=“数量”
});
WriteLine(Zip(拆分(源、字段、字段));
Console.WriteLine(ReplaceRegex(源,字段));
}
静态IEnumerable拆分(字符串源,IEnumerable字段)
{
var指数=0;
foreach(fields.OrderBy中的var字段(q=>q.pos))
{
收益返回源.Substring(index,field.pos-index);
索引=field.pos+field.len;
}
收益率返回源.Substring(index,source.Length-index);
}
静态字符串压缩(IEnumerable拆分,IEnumerable字段)
{
var items=splitted.Zip(字段,(l,r)=>newstring[]{l,r.value});
items.Add(splitted.Last());
返回字符串.Concat(项);
}
静态字符串替换正则表达式(字符串源,IEnumerable字段)
{
var fieldsDict=fields.ToDictionary(q=>q.tag);
var re=new Regex(@“\[(\w+)\]”);
返回re.Replace(源,新MatchEvaluator((m)=>fieldsDict[m.Groups[1].Value].Value));
}
}

顺便说一句,最好使用regex替换特殊的用户标记,如[price]、[qty]。使用
StringBuilder
类可以构建可变字符串。在执行
插入操作之前,请尝试使用
删除
功能。因为它是随机访问的,所以应该非常快。只要
StringBuilder
保持相同的容量,就不需要在内存中复制字符串。如果您知道字符串将变长,请在调用
New StringBuilder()

时尝试将容量设置为更大。我建议使用
StringBuilder
类。然而,你可以用一个字符串,但也可能有副作用。下面是几篇博客文章,展示了如何操作字符串和可能的副作用


如果您的字符串已针对该长度进行了预格式化,则StringBuilder类

public StringBuilder Replace(string oldValue, string newValue, int startIndex, int count)
只需将开始索引和计数设置为1,就可以替换该特定实例

您可以做的另一件事是使用String.Format()。将所有预定义字段转换为索引,这样您就可以得到一个类似“This{0}is very{1}”的字符串,然后只需将参数与特定索引匹配,并执行string.Format(myString,myParams)


-劳尔

一次演一个角色可能是你最好的选择。我这样说是因为在
StringBuilder
上调用
Insert
Remove
会导致字符左右移动,就像在任何可变索引集合(如
列表
中使用的类似方法一样

这就是说,这是一个很好的扩展方法,使您的生活更轻松的候选人

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.
我的名字是Bob。如果替换子字符串将成为一个很大的瓶颈,那么您可能想彻底放弃子字符串的事情。相反,将数据分解为可以独立修改的字符串。如下所示:

class DataLine
{
    public string Field1;
    public string Field2;
    public string Field3;

    public string OutputDataLine()
    {
        return Field1 + Field2 + Field3;
    }
}
这是一个简单的静态示例,但我相信它可以变得更通用,这样,如果每个用户定义的字段不同,您就可以处理它。在将数据拆分为字段后,如果仍然需要修改字段中的单个字符,至少不会涉及整个数据集


现在,这可能会将瓶颈推到OutputDataLine函数,这取决于您对数据的处理。但如有必要,可以单独处理。

正如您所说,StringBuilder有插入方法,但没有覆盖方法。
因此,我为我的项目创建了覆盖扩展方法,请参见下文。
请注意,如果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 ) );
    }

使用
Remove
Insert
将需要移动内存。此外,如果字段定义为(位置、长度)tople,您需要做一些数学计算,如果插入的文本比文本短或长,它将替换。+1我没有想到使用Remove方法
class DataLine
{
    public string Field1;
    public string Field2;
    public string Field3;

    public string OutputDataLine()
    {
        return Field1 + Field2 + Field3;
    }
}
    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 ) );
    }