【问题标题】:Boost C++ regular expression to match SIP header提升 C++ 正则表达式以匹配 SIP 标头
【发布时间】:2015-11-04 08:10:32
【问题描述】:

我们有以下内容来匹配 SIP Via 标头。我们正在使用 boost C++ 正则表达式

Via: SIP/2.0/UDP 192.168.1.4:62486;rport;branch=z9hG4bK-524287-1---3ff9a622846c0a01;stck=3449406834;received=90.206.135.26     

正则表达式:

 std::regex g_Via;

 g_Via("(^Via:\\s+SIP/2\\.0/UDP\\s+)(((\\w|\\.|-)+):(\\d+))((;\\s*rport=(\\d+))|(;stck=(\\d+))|(;[^;\\n\\s]+)*)(\\s*$)",std::regex_constants::icase)

 std::match_results<std::string::const_iterator> result;
 bool valid = std::regex_match(line, result, g_Via);
 if(valid)
 {
    std::string rport = result[8].str();
    std::string stckval = result[9].str();
    // use these values
 }

我们想要的是获取IP地址后面的rport、received和stck参数。我们可以使用上面的表达式获取 IP 地址,但是在获取单个参数时会遇到问题。

rport 参数可以是 ;rport 或 ;rport=14838 即独立或具有价值。

我们遇到的问题是 ;branch= ;received= 等参数可以在不同的位置

【问题讨论】:

  • 您使用哪种语言?
  • ok 更正了 rport 并使其特定于 boost c++ 正则表达式。我们需要获取 rport 和 stck 参数。 rport 可能有值,也可能没有值。

标签: c++ regex boost


【解决方案1】:

我不建议使用正则表达式“解析”SIP 标头。

正如在 cmets 中已经提到的,处理属性变得笨拙。此外,您会发现规范 (rfc 2616/rfc 822) 中有一些微妙的细节很难做到正确。

我之前使用 Boost Spirit 创建了一个 SIP 标头解析器:

我实际上已经直播创建了该解析器。如果您想看的话,这里是直播视频:part #1part #2part #3part #4

在这里使用解析器生成器的好处是您不会得到原始匹配组,而是可以直接解析成对进一步处理有用的东西,例如

using Headers = std::map<std::string, std::string>;

Live On Coliru

//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <map>

using Headers = std::map<std::string, std::string>;

template <typename It> Headers parse_headers(It first, It last) 
{
    using namespace boost::spirit::qi;

    auto& crlf       = "\r\n";
    auto& tspecials = " \t><@,;:\\\"/][?=}{:";

    rule<It, std::string()> token, value;

    token = +~char_(tspecials); // FIXME? should filter CTLs
    value = *(char_ - (crlf >> &(~blank | eoi)));
    BOOST_SPIRIT_DEBUG_NODES((token)(value));

  //value = *(omit[ crlf >> !(~blank | eoi) ] >> attr(' ') | (char_ - crlf));

    Headers headers;
    bool ok = phrase_parse(first, last, (token >> ':' >> value) % crlf >> omit[*lit(crlf)], blank, headers);

#ifdef DEBUG
    if (ok)          std::cerr << "DEBUG: Parse success\n";
    else             std::cerr << "DEBUG: Parse failed\n";
    if (first!=last) std::cerr << "DEBUG: Remaining unparsed input: '" << std::string(first,last) << "'\n";
#endif

    if (ok && (first==last))
        return headers;

    throw std::runtime_error("Parse error in headers\n"); // TODO FIXME
}

int main()
{
    boost::spirit::istream_iterator iter(std::cin >> std::noskipws), end;

    for (auto& header : parse_headers(iter, end)) {
        std::cout << "Key: '" << header.first << "', Value: '" << header.second << "'\n";
    }
}

输入:

Via: SIP/2.0/UDP 10.10.1.99:5060;branch=z9hG4bK343bf628;rport
Contact: <sip:15@10.10.1.99>
Call-ID: 326371826c80e17e6cf6c29861eb2933@10.10.1.99
CSeq: 102 INVITE
User-Agent: Asterisk PBX
Max-Forwards: 70
Date: Wed, 06 Dec 2009 14:12:45 GMT
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY
Supported: replaces
Content-Type: application/sdp
Content-Length: 258
From: "Test 15" <sip:15@10.10.1.99>
 ; tag   =    fromtag
To: <sip:13@10.10.1.13>;tag=totag

打印输出

Key: 'Allow', Value: 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY'
Key: 'CSeq', Value: '102 INVITE'
Key: 'Call-ID', Value: '326371826c80e17e6cf6c29861eb2933@10.10.1.99'
Key: 'Contact', Value: '<sip:15@10.10.1.99>'
Key: 'Content-Length', Value: '258'
Key: 'Content-Type', Value: 'application/sdp'
Key: 'Date', Value: 'Wed, 06 Dec 2009 14:12:45 GMT'
Key: 'From', Value: '"Test 15" <sip:15@10.10.1.99>
 ; tag   =    fromtag'
Key: 'Max-Forwards', Value: '70'
Key: 'Supported', Value: 'replaces'
Key: 'To', Value: '<sip:13@10.10.1.13>;tag=totag'
Key: 'User-Agent', Value: 'Asterisk PBX'
Key: 'Via', Value: 'SIP/2.0/UDP 10.10.1.99:5060;branch=z9hG4bK343bf628;rport'

【讨论】:

  • @Ralf Dagnabbit! livecoding.tv 似乎已经吃掉了这些比特。感谢您的提醒。好在代码还在那里,只是遗憾的是失去了一些评论/讨论:(
  • 谢谢,稍后再调查。在短期内,我将捕获整个 params 组并按令牌拆分它
  • @sehe Darn,看起来很有趣的讨论,可惜!
【解决方案2】:

根据您使用的语言,处理属性可能最好与正则表达式分开处理。您可以使用正则表达式来提取每个属性(或者可能是整个属性字符串——从第一个 ; 到结尾的所有内容)。之后,您可以使用; 作为分隔符来拆分属性字符串。例如,Python 和 PHP 都具有执行此操作的简单函数(Python 中的 split() 和 PHP 中的 explode())。然后,您可以通过= 拆分每个属性,以将属性名称与属性值分开。

【讨论】:

  • 我实际上已经使用 Boost Spirit 创建了这种方法。 Spirit 是 PEG 语法的解析器生成器。它非常适合像这样的“小语法”。 (见my answer,顺便说一句)
【解决方案3】:

您使用了太多我无法跟踪的群组,因此我删除了大部分。这适用于您提到的任何语言。

  • (?&lt;Named&gt; groups) 可能在某些版本中不受支持,但您可以轻松地将其更改为普通的 (group)。我将它们用于实践。

正则表达式

^Via:\s+SIP\/2\.0\/UDP\s+                # header
([-.\w]+):(\d+)                          # IP (group 1) and port (group 2)
(?:                                      # ITERATE
    (?<received>;received=[.\d]+)        #   received (group "received")
  |                                      #
    (?<rport>;rport                      #   rport (group "rport")
        (?:=(?<rportval>[0-9]+))?        #    with optional num (group "rportval")
    )                                    #
  |                                      #
    (?<stck>;stck=\d+)                   #   stck (group "stck")
  |                                      #
    ;[^;\n\s=]+(?:=[^;]+)?               #   any other param (not captured)
)*                                       # Repeat iteration *
\s*$                                     # to EoL

单线:

^Via:\s+SIP\/2\.0\/UDP\s+([-.\w]+):(\d+)(?:(?<received>;received=[.\d]+)|(?<rport>;rport(?:=(?<rportval>[0-9]+))?)|(?<stck>;stck=\d+)|;[^;\n\s=]+(?:=[^;]+)?)*\s*$

代码

使用Boost.Regex

#include <iostream>
#include <boost/regex.hpp>
using namespace std;
using namespace boost;


int main  () {
    string subject = "Via: SIP/2.0/UDP 192.168.1.4:62486;rport=12345;branch=z9hG4bK-524287-1---3ff9a622846c0a01;stck=3449406834;received=90.206.135.26";
    string pattern = "^Via:\\s+SIP/2\\.0/UDP\\s+([-.\\w]+):([0-9]+)(?:(?<received>;received=[.0-9]+)|(?<rport>;rport(?:=(?<rportval>[0-9]+))?)|(?<stck>;stck=[0-9]+)|;[^;\\n\\s=]+(?:=[^;]*)?)*\\s*$";
    smatch match;


    const regex re(pattern);
    if (regex_search(subject, match, re)) {
        string received = match["received"];
        string rport = match["rport"];
        string rportval = match["rportval"];
        string stck = match["stck"];
        cout << "rport = " << rport << endl << "rportval = " << rportval << endl;
    } else {
        cout << "NO MATCH" << endl;
    }
    return 0;
}

输出:

rport = ;rport=12345
rportval = 12345

rextester.com demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-26
    • 2019-10-30
    • 2017-03-21
    • 1970-01-01
    • 2010-10-24
    • 2018-07-17
    • 2011-01-12
    相关资源
    最近更新 更多