通过正则表达式过滤和替换的有效方法

时间:2012-09-04 17:53:37

标签: c# regex

我正在处理一系列字符串,并希望执行以下操作:

//Regex regex; List<string> strList; List<string> strList2; 
foreach (string str in strList){
    if (regex.IsMatch(str)) {      //only need in new array if matches...
        strList2.Add(regex.Replace(str, myMatchEvaluator)) 
                                   //but still have to apply transformation
    }
}

现在,我知道它有效,但这实际上意味着在数组中的每个字符串上运行两次相同的正则表达式。有没有办法将这两个步骤 - 过滤和转换 - 折叠成一个正则表达式解析调用?

(大部分时间都可以使用的是

string str2 = regex.Replace(str, myMatchEvaluator);
if (str2 == str)
    strList2.Add(str2);

但是这通常会抛出一些仍然不需要替换的有效匹配。)

编辑:一个正则表达式示例,与我的大致相似,以说明为什么这很棘手: 想象一下,在日志文件的行开头查找单词,并希望将它们大写。

正则表达式为new Regex("^[a-z]+", RegexOptions.IgnorePatternWhiteSpace),替换函数为match => match.ToUpper()

现在有些第一句话已经大写了,我不想把它们扔掉。另一方面,我不希望大写该行的所有实例,只是第一个。

4 个答案:

答案 0 :(得分:2)

您可以创建自己的匹配评估器:

private class DetectEvaluator {
    public bool HasBeenAvaluated { get; private set }
    private MatchEvaluator evaluator;
    public DetectEvaluator(MatchEvaluator evaluator) { 
        HasBeenAvaluated = false;
        this.evaluator = evaluator;
    }
    public string Evaluate(Match m) {
        HasBeenAvaluated = true;
        return evaluator(m);
    }
}

然后为每个支票创建一个新的:

var de1 = new DetectEvaluator(myMatchEvaluator);
string str2 = regex.Replace(str, de1.Evaluate);
if( de1.HasBeenEvaluated ) strList2.Add(str2);

但我认为这里的可读性没有提高。

答案 1 :(得分:1)

您可以使用lambda函数作为匹配评估程序来更新单词列表。

IEnumerable<string> Replaces(string source)
{
    var rx = new Regex(@"\w+m", RegexOptions.IgnoreCase); // match words ending with 'm'
    var result = new List<string>(); 
    rx.Replace(source, m => { result.Add(m.ToString().ToUpper()); return m.ToString(); });
    return result;
}

    List<string> GetReplacements(List<string> sources) {
        var rx = new Regex(@"\w+m", RegexOptions.IgnoreCase); // match words ending with 'm'.
        var replacements = new List<string>(sources.Count);   // no need to allocate more space than needed.

        foreach(string source in sources) 
            // for each string in sources that matches 'rx', add the ToUpper() version to the result and replace 'source' with itself.
            rx.Replace(source, m  => {replacements.Add(m.ToString().ToUpper()); return m.ToString(); });

        return replacements;
    }

    List<string> GetReplacements2(List<string> sources) {
        var rx = new Regex(@"\w+m", RegexOptions.IgnoreCase); // match words ending with 'm'.
        var replacements = new List<string>(sources.Count);   // no need to allocate more space than needed.

        foreach(string source in sources) {
            var m = rx.Match(source);                         // do one rx match
            if (m.Success)                                    // if successfull
                replacements.Add(m.ToString().ToUpper());     // add to result.
        }

        return replacements;
    }

如果您需要修改原始源并收集未修改的匹配项,则交换lambda表达式中的部分。

答案 2 :(得分:0)

这样的事情会起作用吗?

foreach (string str in strList)
{
    str = regex.Replace(str, delegate(Match thisMatch) {
        // only gets here if matched the regex already
        string str2 = yourReplacementFunction(thisMatch);  
        strList2.Add(str2);

        return thisMatch.Value;

    }); 
}

答案 3 :(得分:0)

根据我收到的所有答案,以下工作:

void AddToIfMatch(List<string> list, string str; Regex regex; 
                                        MatchEvaluator evaluator)
{
    bool hasBeenEvaluated = false;
    string str2 = regex.Replace(
        str, 
        m => {HasBeenEvaluated = true; return evaluator(m);}
    );
    if( hasBeenEvaluated ) {list.Add(str2);}
}