如何替换正则表达式中的匹配组?

How do I replace matched groups in a regex?

给定一些输入数据:

<somexml>
    <User Name="MrFlibble">
        <Option Name="Pass">SomeSaltedPassword</Option>
        <Option Name="Salt">Salt</Option>
        <tag1></tag1>
        <Permissions>
            <Permission Dir="E:"></Permission>
        </Permissions>
    </User>
    <User Name="MrFlobble">
        <Option Name="Pass">SomeOtherSaltedPassword</Option>
        <Option Name="Salt">Salt</Option>
        <tag1></tag1>
        <Permissions>
            <Permission Dir="C:"></Permission>
        </Permissions>
    </User>
</somexml>

我想用 Jon 和 [=16] 替换用户区中第一个没有 C: 权限的用户(在本例中 MrFlibble) =] 和 MyNewSaltedPassword 使用 .net 框架正则表达式给出以下结果:

<somexml>
    <User Name="Jon">
        <Option Name="Pass">MyNewSaltedPassword</Option>
        <Option Name="Salt">Salt</Option>
        <tag1></tag1>
        <Permissions>
            <Permission Dir="E:"></Permission>
        </Permissions>
    </User>
    <User Name="MrFlobble">
        <Option Name="Pass">SomeOtherSaltedPassword</Option>
        <Option Name="Salt">Salt</Option>
        <tag1></tag1>
        <Permissions>
            <Permission Dir="C:"></Permission>
        </Permissions>
    </User>
</somexml>

我认为像这样的正则表达式会捕获用户并对我要替换的部分进行分组:

<User Name="(.*)">.*<Option Name="Pass">(.*)<\/Option>.*<Option Name="Salt">(.*)<\/Option>.*<\/User>

...但我正在努力了解如何在保留其他文本的同时替换这三个组。 The docs 似乎都建议用特定的新文本替换原始文本的修改,而不是多个特定命名的组。

有没有标准的方法可以做到这一点,还是我找错树了?

在任何情况下都不要尝试用正则表达式解析 XML 除非你想调用 rite 666 Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn .

使用 XML 解析库,请参阅 this page 了解一些方法。

使用正则表达式很难做到这一点,因为您需要按条件进行替换。

在您写的评论中,它的格式正确 xml。因此,我敢于提供使用 xml 解析器的解决方案。

将对 System.Xml.Linq 库的引用添加到项目中。
打开以下命名空间

using namespace System;
using namespace System::IO;
using namespace System::Xml::Linq;

代码非常简洁

//auto xml = XElement::Parse(input); // input - string containing your xml
auto xml = XElement::Load(L"test.xml");

for each (auto user in xml->Elements(L"User"))
{
    if (user->Element(L"Permissions")->Element(L"Permission")->Attribute(L"Dir")->Value != L"C:")
    {
        user->Attribute(L"Name")->Value = L"Jon";

        for each(auto option in user->Elements(L"Option"))
        {
            if (option->Attribute(L"Name")->Value == L"Pass")
            {
                option->Value = L"MyNewSaltedPassword";
            }
        }
    }
}

Console::WriteLine(xml);
//xml->Save(L"result.xml");

带有正则表达式的选项。表达式本身看起来晦涩难懂,因此很难维护。因此,最好将方法与 xml 解析器一起使用。

using namespace System;
using namespace System::IO;
using namespace System::Text::RegularExpressions;

MatchEvaluator 方法:

String^ Evaluate(Match^ m)
{
    if (m->Groups[L"dir"]->Value != L"C:")
        return L"Jon" + m->Groups[L"mid1"] + L"MyNewSaltedPassword" + m->Groups[L"mid2"] + m->Groups[L"dir"];
    else
        return m->Groups[L"name"]->Value + m->Groups[L"mid1"] + m->Groups[L"pass"] + m->Groups[L"mid2"] + m->Groups[L"dir"];
}

代码:

auto input = File::ReadAllText(L"test.xml");

auto pattern = gcnew String(R"(
(?<= <User \s Name = " )
(?'name' .+? )
(?= "> )

(?'mid1' .+? )

(?<= <Option \s Name = "Pass"> )
(?'pass' .+? )
(?= </Option> )

(?'mid2' .+? )

(?<= <Permission \s Dir = " )
(?'dir' .+? )
(?= "> )
)");

auto options = RegexOptions::IgnorePatternWhitespace | RegexOptions::Singleline;

auto evaluator = gcnew MatchEvaluator(Evaluate);
auto result = Regex::Replace(input, pattern, evaluator, options);

Console::WriteLine(result);