在没有内存泄漏的情况下替换大字符串中的文本

时间:2012-07-31 13:19:50

标签: c# .net memory-leaks replace

我目前正在制作一个必须连续生成约16000封电子邮件的批次(简报)。

无论是垃圾邮件还是垃圾邮件,我的问题都是关于如何生成这些电子邮件。

邮件中的某些字段必须替换为自定义值(当天的日期,用户的名称等)。

对于某些截止日期和代码可重用性原因,我的模板是一个HTML文件,其中包含一些“_FIELDNAME”字段,可以通过正则表达式轻松识别:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
...
<body>
<p>Hi _NAME, _DATE newsletter.</p>
</body>
...

该文件约为1000行,因此在加载时它是一个非常大的字符串。

首先,我在字符串中加载一次HTML文件模板:

string template = File.ReadAllText(@"Template/newsletter.html");

替换功能如下所示:

return new StringBuilder(template)
.Replace("_DATE", profileConfig.SelectedMonth.ToString("MMMM yyyy"))
.Replace("_NAME", profileConfig.Name)
.ToString();

问题是每次迭代内存消耗略有增加。 1000次迭代大约50MB,这是由于我的替换功能(我试图评论它,内存泄漏消失了)。

如何在模板中替换多个字段(~50)而不会溢出内存以进行16000次迭代? 我试了几个东西,比如使用正则表达式(但它使用字符串)或临时文件,但两个都不满意我。

提前感谢您的帮助。

3 个答案:

答案 0 :(得分:3)

如果您可以使用_DATE_NAME等替换{0}{1}等,则可以尝试string.Format()

模板将成为:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
...
<body>
<p>Hi {0}, {1} newsletter.</p>
</body>
...

代码看起来像这样:

return string.Format(template, 
        profileConfig.SelectedMonth.ToString("MMMM yyyy"), 
        profileConfig.Name
    );

您实际上根本不需要经历StringBuilder。如果你去了File.ReadAllLines()并且只在包含标记的行中交换了值,那么你的速度(以及可能在资源使用方面)会大大受益。

更新为了强制使用string.Format(string format, params object[] args)重载,您可能必须将所有参数放入集合中。

以下内容应该使这个解决方案适合您(我测试了多达1000个参数,并且它的工作非常快)。

List<string> tokenValues = new List<string> 
{ 
    profileConfig.SelectedMonth.ToString("MMMM yyyy"), 
    profileConfig.Name, 
    <follow with your other values>
};
return string.Format(template, tokenValues.ToArray()); //.ToArray() is mandatory

答案 1 :(得分:1)

    var patterns = new Dictionary<string, string>();
    patterns["_Date"] = profileConfig.SelectedMonth.ToString("MMMM yyyy");
    patterns["_Name"] = profileConfig.Name;

    var builder = new StringBuilder(template.Length);
    for (var i = 0; i < template.Length;)
    {
      var pattern = CompareAndFindPattern(template, i, patterns);
      if (pattern != null)
      {
        builder.Append(pattern.Value.Value);
        i += pattern.Value.Key.Length;
      }
      else
      {
        builder.Append(template[i]);
        i++;
      }
    }

  static KeyValuePair<string, string>? CompareAndFindPattern(string template, int index, Dictionary<string, string> patterns)
  {
    foreach (var pattern in patterns)
    {
      if (string.Compare(template, index, pattern.Key, 0, pattern.Key.Length) == 0)
        return pattern;
    }
    return null;
  }

答案 2 :(得分:0)

尝试了很多解决方案后,我终于决定从头开始重新启动我的批处理。

我现在使用适当的XSLT文件从XML配置生成HTML。

内存消耗量会随着时间的推移而增加但现在变慢了。我想垃圾收集器不想收集,因为我的计算机有6GB内存,没有其他大型进程可以运行。