0

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

//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

4 回答 4

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);

但我没有看到这里提高了可读性。

于 2012-09-04T18:21:39.047 回答
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 表达式中的部分。

于 2012-09-04T19:03:27.253 回答
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;

    }); 
}
于 2012-09-04T18:05:12.163 回答
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);}
}
于 2012-09-05T16:28:11.910 回答