我必须处理一个大文件(几MB)并从中删除标记有时间的注释。一个例子:
blablabla 12:10:40 I want to remove this
blablabla some more
even more bla
过滤后,我希望它看起来像这样:
blablabla
blablabla some more
even more bla
最好的方法应该是放宽正则表达式:
Dataout = Regex.Replace(Datain, "[012][0123456789]:[012345][0123456789]:[012345][0123456789].*", string.Empty, RegexOptions.Compiled);
现在这完全适用于我的目的,但它有点慢..我假设这是因为前两个字符[012]和[0123456789]匹配了很多数据(它是一个包含十六进制的ASCII文件数据,所以像“0045ab0123”等。)。所以Regex经常匹配前两个角色。
当我将正则表达式更改为
时Dataout = Regex.Replace(Datain, ":[012345][0123456789]:[012345][0123456789].*", string.Empty, RegexOptions.Compiled);
这是一个巨大的加速,可能是因为文件中没有多少':'。好!但是我仍然需要在第一个':'之前检查两个字符,然后删除该行的其余部分。
所以我的问题归结为:
或许还有更好的方法?
答案 0 :(得分:2)
您可以在问题中使用两个正则表达式。首先匹配前导冒号表达式以快速查找或排除可能的行。如果成功,则使用完整替换表达式。
MatchCollection mc = Regex.Matches(Datain, ":[012345][0123456789]:[012345][0123456789].*"));
if ( mc != null && mc.Length > 0 )
{
Dataout = Regex.Replace(Datain, "[012][0123456789]:[012345][0123456789]:[012345][0123456789].*", string.Empty, RegexOptions.Compiled);
}
else
{
Dataout = Datain;
}
变体可能是
Regex finder = new Regex(":[012345][0123456789]:[012345][0123456789].*");
Regex changer = new regex("[012][0123456789]:[012345][0123456789]:[012345][0123456789].*");
if ( finder.Match(Datain).Success)
{
Dataout = changer.Replace(Datain, string.Empty);
}
else
{
Dataout = Datain;
}
另一种变体是使用上述finder
。如果找到该字符串,则只需检查前两个字符是否为数字。
Regex finder = new Regex(":[012345][0123456789]:[012345][0123456789].*");
Match m = finder.Match(Datain);
if ( m.Success && m.Index > 1)
{
if ( char.IsDigit(DataIn[m.index-1]) && char.IsDigit(DataIn[m.index-2])
{
Dataout = m.Index-2 == 0 ? string.Empty : DataIn.Substring(0, m.Index-2);
}
else
{
Dataout = Datain;
}
}
else
{
Dataout = Datain;
}
在第二和第三个想法中,finder
和changer
应该在读取任何行之前声明并给定值。无需在行读取循环内执行new Regex(...)
。
答案 1 :(得分:0)
您可以使用DateTime.TryParseExact
来检查某个单词是否是时间,然后再记录所有单词。这是一个LINQ查询,用于清除路径中的所有行,也许它更有效:
string format = "HH:mm:ss";
DateTime time;
var cleanedLines = File.ReadLines(path)
.Select(l => string.Join(" ", l.Split().TakeWhile(w => w.Length != format.Length
|| !DateTime.TryParseExact(w, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out time))));
如果性能非常关键,您还可以创建针对此任务优化的专用方法。这是一种应该更有效的方法:
public static string SubstringBeforeTime(string input, string timeFormat = "HH:mm:ss")
{
if (string.IsNullOrWhiteSpace(input))
return input;
DateTime time;
if (input.Length == timeFormat.Length && DateTime.TryParseExact(input, timeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out time))
{
return ""; // full text is time
}
char[] wordSeparator = {' ', '\t'};
int lastIndex = 0;
int spaceIndex = input.IndexOfAny(wordSeparator);
if(spaceIndex == -1)
return input;
char[] chars = input.ToCharArray();
while(spaceIndex >= 0)
{
int nonSpaceIndex = Array.FindIndex<char>(chars, spaceIndex + 1, x => !wordSeparator.Contains(x));
if(nonSpaceIndex == -1)
return input;
string nextWord = input.Substring(lastIndex, spaceIndex - lastIndex);
if( nextWord.Length == timeFormat.Length
&& DateTime.TryParseExact(nextWord, timeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out time))
{
return input.Substring(0, lastIndex);
}
lastIndex = nonSpaceIndex;
spaceIndex = input.IndexOfAny(wordSeparator, nonSpaceIndex + 1);
}
return input;
}
样本数据和测试:
string[] lines = { "blablabla 12:10:40 I want to remove this", "blablabla some more", "even more bla ", "14:22:11" };
foreach(string line in lines)
{
string newLine = SubstringBeforeTime(line, "HH:mm:ss");
Console.WriteLine(string.IsNullOrEmpty(newLine) ? "<empty>" : newLine);
}
输出:
blablabla
blablabla some more
even more bla
<empty>
答案 2 :(得分:0)
最后我去了这个:
bool MeerCCOl = true;
int startpositie = 0;
int CCOLfound = 0; // aantal keer dat terminal output is gevonden
while(MeerCCOl)
{
Regex rgx = new Regex(":[0-5][0-9]:[0-5][0-9]", RegexOptions.Multiline | RegexOptions.Compiled);
Match GevondenColon = rgx.Match(VlogDataGefilterd,startpositie);
MeerCCOl = GevondenColon.Success; // CCOL terminal data gevonden, er is misschien nog meer..
if (MeerCCOl && GevondenColon.Index >= 2)
{
CCOLfound++;
int GevondenUur = 10 * (VlogDataGefilterd[GevondenColon.Index - 2] - '0') +
VlogDataGefilterd[GevondenColon.Index - 1] - '0';
if (VlogDataGefilterd[GevondenColon.Index - 2] >= '0' && VlogDataGefilterd[GevondenColon.Index - 2] <= '2' &&
VlogDataGefilterd[GevondenColon.Index - 1] >= '0' && VlogDataGefilterd[GevondenColon.Index - 1] <= '9' &&
GevondenUur>=0 && GevondenUur<=23)
{
Regex rgx2 = new Regex("[012][0-9]:[0-5][0-9]:[0-5][0-9].*", RegexOptions.Multiline);
VlogDataGefilterd = rgx2.Replace(VlogDataGefilterd, string.Empty, 1, (GevondenColon.Index - 2));
startpositie = GevondenColon.Index - 2; // start volgende match vanaf de plek waar we de
}
}
}
首先搜索匹配到:xx:xx,然后检查之前的2个字符。如果它被识别为一个时间它将删除整个事物。奖金是通过单独检查小时,我可以确保小时数读取00-23,而不是00-29。比赛次数也是这样计算的。
原始的简单正则表达式大约需要550毫秒。对于相同的数据文件,此代码(虽然更麻烦)大约需要12毫秒。这是一个惊人的40倍加速: - )
全部谢谢!