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

时间:2019-02-01 16:30:28

标签: .net regex c++-cli

给出一些输入数据:

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

我想用C:MrFlibble替换用户区域(在本例中为Jon)中没有SomeSaltedPassword权限的第一个用户与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似乎都建议用特定的新文本代替对原始文本的修改,而不是用多个特定命名的组。

是否有执行此操作的标准方法?还是我吠叫了错误的树?

3 个答案:

答案 0 :(得分:1)

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

使用XML解析库,请参见this page,了解某些实现方法。

答案 1 :(得分:1)

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

在你写的,它是格式良好的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");

答案 2 :(得分:1)

带有正则表达式的选项。该表达式本身看起来晦涩难懂,因此很难维护。因此,最好将该方法与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);