字符串的耗时操作
在一个新闻采集的项目中,处理一条信息时CPU竟然耗尽,后查找原因发现在一个do...while循环里耗时竟超过20分钟。一般而言,一百万的循环耗时一般为一秒。因此如果一个循环就能将CPU耗尽长达20多分钟,那么循环里一定会有其它非循环操作在作怪。原处理方法如下:
/// <summary>
/// 格式化Html正文.
/// </summary>
/// <param name="Content">Html文档内容。</param>
/// <param name="Buf">结果缓冲区。</param>
public static void FormatHtml(string Content, HtmlBufBase Buf)
{
if (string.IsNullOrEmpty(Content))
return;
// 过滤前面未处理的标记
int TagEnd = Content.IndexOf('>', 0);
if (TagEnd == -1)
TagEnd = 0;
else
{
//处理Title中的HTML标签
int pos = GetNextTagStartPosition(Content, 0);
if (pos > -1 && TagEnd > pos)
TagEnd = 0;
else
TagEnd++;
}
bool bIsNotInHideTag = true; // 是否不在隐藏的标记内容中
bool bIsNotInStyle = true;
string HideTag = null;
string LastTag = null;
do
{
if (Content.Contains("国家统计局服务业调查中心"))
{
}
// 找标记开始
int TagStart = GetNextTagStartPosition(Content, TagEnd);
if (TagStart == -1)
TagStart = Content.Length;// 达到结尾
else if (Content.IndexOf("!--", TagStart) == TagStart+1) // 判断是否是注释
{
TagEnd = Content.IndexOf("-->", TagStart)+3;
if (TagStart == -1)
TagStart = Content.Length;
continue;
}
// 转换内容
if (bIsNotInHideTag && bIsNotInStyle)
Buf.AppendText(Content.Substring(TagEnd, TagStart - TagEnd));
// 找标记结束
TagEnd = GetNextTagEndPosition(Content, TagStart);
if (TagEnd > -1)
{
TagEnd++;
TagStart++;
string TagInfo;
string Tag = GetTagAndTagInfo(Content, TagEnd, TagStart, out TagInfo);
if (Tag == "script") // 脚本开始
{
int scriptEndPos = Content.IndexOf("</script>", TagStart, StringComparison.OrdinalIgnoreCase);
if (scriptEndPos > 0)
{
TagEnd = scriptEndPos + 9;
}
else
{
TagEnd = Content.Length;
}
}
else if (Tag == "style")
{
bIsNotInStyle = false;
}
else if (Tag == "/style")
{
bIsNotInStyle = true;
}
else if (TagInfo.ToLower().IndexOf("display:none") > 0)
{
bIsNotInHideTag = false;
HideTag = Tag;
}
else if (Tag == "/" + HideTag)
{
bIsNotInHideTag = true;
HideTag = null;
}
else
Buf.AppendTag(Tag, TagInfo);
//else if (Tag == "base")
// BaseUrl = GetBaseUrl(TagInfo);
LastTag = Tag;
}
else
break;
} while (true);
Buf.ReFormatText();
}
最近查找到有一条语句特别耗时即
else if (Content.IndexOf("!--", TagStart) == TagStart+1) // 判断是否是注释,这个字符串的查找都是全文查找,肯定特别耗时,这条语句的功能是判断标签是否是注释,因此没必要去判断”!--“只需要判断!即可。因此使用字符查找即可,改动如下:
else if (Content[TagStart+1] == '!') // 判断是否是注释
改成上面语句后CPU尽管会耗尽,但耗时只需30秒。
深入追究一下,可知字符的查找应该是数组的查找,为什么不直接使用数组下标呢?改动如下:
Content[TagStart + 1] == '!'
改后测试一下,在本方法中的两万多次循环中只需要60毫秒。
最后程序改动如下:
/// <summary>
/// 格式化Html正文.
/// </summary>
/// <param name="Content">Html文档内容。</param>
/// <param name="Buf">结果缓冲区。</param>
public static void FormatHtml(string Content, HtmlBufBase Buf)
{
if (string.IsNullOrEmpty(Content))
return;
// 过滤前面未处理的标记
int TagEnd = Content.IndexOf('>', 0);
if (TagEnd == -1)
TagEnd = 0;
else
{
//处理Title中的HTML标签
int pos = GetNextTagStartPosition(Content, 0);
if (pos > -1 && TagEnd > pos)
TagEnd = 0;
else
TagEnd++;
}
bool bIsNotInHideTag = true; // 是否不在隐藏的标记内容中
bool bIsNotInStyle = true;
string HideTag = null;
string LastTag = null;
do
{
// 找标记开始
int TagStart = GetNextTagStartPosition(Content, TagEnd);
if (TagStart == -1)
TagStart = Content.Length;// 达到结尾
// 转换内容
if (bIsNotInHideTag && bIsNotInStyle)
Buf.AppendText(Content.Substring(TagEnd, TagStart - TagEnd));
// 判断是否是注释
if (TagStart != -1 && TagStart < Content.Length && Content[TagStart + 1] == '!')
{
TagEnd = Content.IndexOf("-->", TagStart);
if (TagEnd == -1)//没有找到注释的结束标记
{
TagEnd = Content.Length;
}
else//有注释的结束标记,跳过本标记
{
TagEnd = TagEnd + 3;
continue;
}
}
// 找标记结束
TagEnd = GetNextTagEndPosition(Content, TagStart);
if (TagEnd > -1)
{
TagEnd++;
TagStart++;
string TagInfo;
string Tag = GetTagAndTagInfo(Content, TagEnd, TagStart, out TagInfo);
if (Tag == "script") // 脚本开始
{
int scriptEndPos = Content.IndexOf("</script>", TagStart, StringComparison.OrdinalIgnoreCase);
if (scriptEndPos > 0)
{
TagEnd = scriptEndPos + 9;
}
else
{
TagEnd = Content.Length;
}
}
else if (Tag == "style")
{
bIsNotInStyle = false;
}
else if (Tag == "/style")
{
bIsNotInStyle = true;
}
else if (TagInfo.ToLower().IndexOf("display:none") > 0)
{
bIsNotInHideTag = false;
HideTag = Tag;
}
else if (Tag == "/" + HideTag)
{
bIsNotInHideTag = true;
HideTag = null;
}
else
Buf.AppendTag(Tag, TagInfo);
//else if (Tag == "base")
// BaseUrl = GetBaseUrl(TagInfo);
LastTag = Tag;
}
else
break;
} while (true);
Buf.ReFormatText();
}