【问题标题】:Parse HTTP request without using regexp不使用正则表达式解析 HTTP 请求
【发布时间】:2015-02-01 21:37:48
【问题描述】:

我正在使用正则表达式来分隔 HTTP 请求的字段:

GET /index.asp?param1=hello&param2=128 HTTP/1.1

这边:

smatch m;
try 
{ 
    regex re1("(GET|POST) (.+) HTTP"); 
    regex_search(query, m, re1); 
} 
catch (regex_error e) 
{ 
    printf("Regex 1 Error: %d\n", e.code()); 
}
string method = m[1]; 
string path = m[2];

try 
{ 
    regex re2("/(.+)?\\?(.+)?"); 
    if (regex_search(path, m, re2)) 
    { 
        document = m[1]; 
        querystring = m[2];
    }
} 
catch (regex_error e) 
{ 
    printf("Regex 2 Error: %d\n", e.code()); 
}

不幸的是,此代码在 MSVC 中有效,但不适用于 GCC 4.8.2(我在 Ubuntu Server 14.04 上拥有)。您能否建议一种使用普通 std::string 运算符拆分该字符串的不同方法?

由于查询字符串分隔符“?”,我不知道如何将 URL 拆分为不同的元素。字符串中可能出现也可能不出现。

【问题讨论】:

  • 您可以考虑使用 boost 正则表达式库 (boost.org/doc/libs/1_57_0/libs/regex/doc/html/index.html)
  • 请提供更多信息。如果(您提到的)查询字符串分隔符丢失,您的代码将无法工作。那么两个平台上的输入是什么? @Christophe:总是指向 boost 可能并不总是一个好的提示。
  • @St0fF 对不起,但他的代码可以工作:我将它剪切并粘贴到 MSVC2013 并得到了预期的结果(method="GET", document="index.asp", queerrystring="param1=你好&param2=128")
  • @MarkMiles 你能告诉我们什么不起作用吗?我在 ideone (ideone.com/QZqQM1) 上测试了您的代码,它还返回了正确的结果(使用 gcc 4.9.2)
  • 正如我在帖子中所写:“不幸的是,此代码在 MSVC 中有效,但不适用于 GCC 4.8.2”。它必须在 Ubuntu 14.04 上运行。如果我这样做 #gcc --version 它说它是 4.8.2 但如果我这样做 apt search gcc-4.9 我得到 gcc-4.9-base/trusty,now 4.9-20140406-0ubuntu1 armhf [installed] GCC, the GNU Compiler Collection (base package) 所以我不知道如何更新我的 gcc。

标签: c++ string parsing httprequest


【解决方案1】:

你可以使用std::istringstream 来解析这个:

int main()
{
    std::string request = "GET /index.asp?param1=hello&param2=128 HTTP/1.1";

    // separate the 3 main parts

    std::istringstream iss(request);

    std::string method;
    std::string query;
    std::string protocol;

    if(!(iss >> method >> query >> protocol))
    {
        std::cout << "ERROR: parsing request\n";
        return 1;
    }

    // reset the std::istringstream with the query string

    iss.clear();
    iss.str(query);

    std::string url;

    if(!std::getline(iss, url, '?')) // remove the URL part
    {
        std::cout << "ERROR: parsing request url\n";
        return 1;
    }

    // store query key/value pairs in a map
    std::map<std::string, std::string> params;

    std::string keyval, key, val;

    while(std::getline(iss, keyval, '&')) // split each term
    {
        std::istringstream iss(keyval);

        // split key/value pairs
        if(std::getline(std::getline(iss, key, '='), val))
            params[key] = val;
    }

    std::cout << "protocol: " << protocol << '\n';
    std::cout << "method  : " << method << '\n';
    std::cout << "url     : " << url << '\n';

    for(auto const& param: params)
        std::cout << "param   : " << param.first << " = " << param.second << '\n';
}

输出:

protocol: HTTP/1.1
method  : GET
url     : /index.asp
param   : param1 = hello
param   : param2 = 128

【讨论】:

  • 我接受这个作为答案,因为也解决了我拆分查询字符串的问题,谢谢!
【解决方案2】:

它不能与 gcc 4.8.2 一起工作的原因是 regex_search 没有在 stdlibc++ 中实现。如果您查看regex.h 内部,您会得到以下信息:

template<typename _Bi_iter, typename _Alloc,
    typename _Ch_type, typename _Rx_traits>
inline bool
regex_search(_Bi_iter __first, _Bi_iter __last,
        match_results<_Bi_iter, _Alloc>& __m,
        const basic_regex<_Ch_type, _Rx_traits>& __re,
        regex_constants::match_flag_type __flags
        = regex_constants::match_default)
{ return false; }

改用regex_match,它已实现。您必须修改您的正则表达式(例如,在之前和之后添加 .*),因为 regex_match 匹配整个字符串。

替代方案:

  1. 升级到 gcc 4.9
  2. 改用 boost::regex
  3. 切换到 LLVM 和 libc++(我的偏好)。

【讨论】:

    【解决方案3】:

    如果你想避免使用正则表达式,你可以使用标准的字符串操作:

    string query = "GET / index.asp ? param1 = hello&param2 = 128 HTTP / 1.1";
    string method, path, document, querystring;
    
    try {
        if (query.substr(0, 5) == "GET /")   // First check the method at the beginning 
            method = "GET";
        else if (query.substr(0, 6) == "POST /")
            method = "POST";
        else  throw std::exception("Regex 1 Error: no valid method or missing /");
    
        path = query.substr(method.length() + 2);  // take the rest, ignoring whitespace and slash
        size_t ph = path.find(" HTTP");     // find the end of the url
        if (ph == string::npos)            // if it's not found => error
            throw std::exception("Regex 2 Error: no HTTP version found");
        else path.resize(ph);             // otherwise get rid of the end of the string 
    
        size_t pq = path.find("?");      // look for the ? 
        if (pq == string::npos) {        // if it's absent, document is the whole string
            document = path;
            querystring = "";
        }
        else {                          // orherwie cut into 2 parts 
            document = path.substr(0, pq);
            querystring = path.substr(pq + 1);
        }
    
        cout << "method:     " << method << endl
            << "document:   " << document << endl
            << "querystring:" << querystring << endl;
    }
    catch (std::exception &e) {
        cout << e.what();
    }
    

    当然,这段代码不如你原来的正则表达式基代码那么好。因此,如果您不能使用最新版本的编译器,这将被视为一种解决方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-05
      • 1970-01-01
      • 2015-07-08
      • 2012-08-23
      相关资源
      最近更新 更多