什么是正确的正则表达式模式来解析HTTP的摘要式身份验证?

时间:2018-02-02 02:11:34

标签: c++ regex

我有一个解析HTTP摘要组件的程序,如下所示:

#include "stdafx.h"
#include <iostream>
#include <string>
#include <regex>
#include <unordered_map>

int main()
{
    std::string nsInput = R"(Digest realm = "http-auth@example.org",
        qop= " auth, auth-int ", algorithm = MD5 ,
        nonce ="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v"    ,
        opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS"
    )";
    //  Spaces are inserted into some places of the input intentionally

    std::smatch mat_opt, mat_val;
    std::unordered_map<std::string, std::string> mapDigest;

    try {
        std::regex rex_opt(R"(\s*([A-Za-z]{3,})\s*=)");
        std::regex rex_val(R"(\s*\"\s*(.{3,})\s*\"|\s*(.{3,})\s*,)");

        auto& str = nsInput;
        while (std::regex_search(nsInput, mat_opt, rex_opt))
        {
            if (mat_opt.size() >= 2) {
                auto& field = mat_opt[1].str();
                std::string& next = mat_opt.suffix().str();

                if (std::regex_search(next, mat_val, rex_val) && mat_val.size() >= 2) {
                    auto& value = mat_val[1].str();
                    mapDigest[field] = value;
                }

                str = mat_opt.suffix().str();
            }
        }

        for (auto& itr : mapDigest) {
            std::cout << itr.first << ":" << itr.second << ".\n";
        }
    }
    catch (std::regex_error& e) {
        std::cout << "regex_search failed" << e.what() << "\n";
    }

    return 0;
}

输出:

nonce:7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v.
realm:http-auth@example.org.
qop:auth, auth-int .
algorithm:.
opaque:FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS.

我想解决的是:

1)空格仍然出现在“qop”值的末尾。

2)“算法”的值无法匹配。

可能有人为这个不起眼的事业以及如何修复它而发光?

由于

2 个答案:

答案 0 :(得分:1)

首先,您的代码无法编译,因为您尝试将非const左值引用绑定到以下行中的临时对象:

// ...
auto& field = mat_opt[1].str();
// ...
std::string& next = mat_opt.suffix().str();
// ...
auto& value = mat_val[1].str();
// ...

我建议删除引用,然后使用autostd::string代替。由于RVO,它几​​乎没有性能损失。

要删除值末尾的空格,可以在正则表达式模式中使用.{3,}?代替.{3,}。没有.{3,}的{​​{1}}将贪婪地匹配,因此将匹配所有跟随的字符(包括空格)。

字符串?与正则表达式模式中的第二个括号匹配,因此您应该通过MD5而不是mat_val[2]来访问它。您可以按如下方式使用条件表达式:

mat_val[1]

BTW,因为您使用的是原始字符串文字,所以不需要在正则表达式模式中的字符auto value = mat_val[1].matched ? mat_val[1].str() : mat_val[2].str(); 之前写一个额外的\

答案 1 :(得分:1)

正如其他人所说,正则表达式可能不是解析HTTP摘要的首选武器。

然而,我发现这种模式具有挑战性。更糟糕的是,你的引号中的分隔符应该被忽略(在qop-part中)。你的其他问题源于贪婪的比赛(例如{3,} - 部分)。

无论如何,这是我15分钟后得到的:

=\s*((?:[^,"]|"\s*([^"]*?)\s?")+?)(?=\s*,|$)

Demo

更新:我加倍努力 - 只是为了证明我的观点。

#include <iostream>
#include <string>
#include <regex>
#include <unordered_map>

int main()
{
    std::string nsInput = R"(Digest realm = "http-auth@example.org",
        qop= " auth, auth-int ", algorithm = MD5 ,
        nonce ="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v"    ,
        opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS"
    )";
    //  Spaces are inserted into some places of the input intentionally

    std::smatch mat_opt, mat_val;
    std::unordered_map<std::string, std::string> mapDigest;

    try {
        std::regex rex_opt(R"(\s*([A-Za-z]{3,})\s*=)");
        std::regex rex_val("=\\s*((?:[^,\"]|\"\\s*([^\"]*?)\\s?\")+?)(?=\\s*,|$)");

        auto& str = nsInput;
        while (std::regex_search(nsInput, mat_opt, rex_opt))
        {
            if (mat_opt.size() >= 2) {
                auto field = mat_opt[1].str();

                if (std::regex_search(nsInput, mat_val, rex_val)) {
                    auto value = mat_val[2].matched ? mat_val[2].str() : mat_val[1].str();
                    mapDigest[field] = value;
                }

                str = mat_opt.suffix().str();
            }
        }

        for (auto& itr : mapDigest) {
            std::cout << itr.first << ":" << itr.second << ".\n";
        }
    }
    catch (std::regex_error& e) {
        std::cout << "regex_search failed" << e.what() << "\n";
    }

    return 0;
}

输出:

opaque:FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS.
nonce:7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v.
algorithm:MD5.
realm:http-auth@example.org.
qop:auth, auth-int.