【问题标题】:Easy way to parse a url in C++ cross platform?在 C++ 跨平台中解析 url 的简单方法?
【发布时间】:2011-02-06 15:27:44
【问题描述】:

我需要解析一个 URL 以在我用 C++ 编写的应用程序中获取协议、主机、路径和查询。该应用程序旨在跨平台。我很惊讶在boostPOCO 库中找不到任何这样做的东西。是不是很明显我没在看?有关适当的开源库的任何建议?或者这是我必须自己做的事情?它不是超级复杂,但它似乎是一个常见的任务,我很惊讶没有一个通用的解决方案。

【问题讨论】:

  • C++(甚至 C)与其他一些语言不同。这不是标准库默认存在于阳光下的所有东西的那种事情。可能有一些常用的库,但从标准库、语言特性,甚至 POSIX 等特定于操作系统的 API 的角度来看,假设您可以自己做很多事情。
  • 我很高兴能造一个轮子,但如果其他人做过,我就看不出它的意义所在。因此我的问题。你说得对,“可能有一些常用的库”——这就是我要问的。
  • 这是您在代码库所依赖的大框架中找到的那种小实用程序。如果不存在,那么编写一个小的 URL 实用程序集合是一个有趣的标准算法练习。
  • 要使用 RFC 3986 标准解析 URL,无需导入任何新库,只需查看相关问题的答案:stackoverflow.com/a/31613265/1043704

标签: c++ url uri


【解决方案1】:

有一个库建议包含在 Boost 中,并允许您轻松解析 HTTP URI。它使用 Boost.Spirit 并且也在 Boost 软件许可证下发布。该库是 cpp-netlib,您可以在 http://cpp-netlib.github.com/ 找到它的文档——您可以从 http://github.com/cpp-netlib/cpp-netlib/downloads 下载最新版本。

您要使用的相关类型是boost::network::http::uri,并记录在here

【讨论】:

  • 出于过时的原因添加注释:由于 Boost 不推荐使用 get_io_service,该库在 2020 年不再按预期使用最新版本的 Boost 进行编译。但是,您仍然可以从 carcass 中提取相关功能,因为它是独立的,不依赖于库的那些部分。
【解决方案2】:

上面的 Wstring 版本,添加了我需要的其他字段。绝对可以改进,但对我的目的来说已经足够了。

#include <string>
#include <algorithm>    // find

struct Uri
{
public:
std::wstring QueryString, Path, Protocol, Host, Port;

static Uri Parse(const std::wstring &uri)
{
    Uri result;

    typedef std::wstring::const_iterator iterator_t;

    if (uri.length() == 0)
        return result;

    iterator_t uriEnd = uri.end();

    // get query start
    iterator_t queryStart = std::find(uri.begin(), uriEnd, L'?');

    // protocol
    iterator_t protocolStart = uri.begin();
    iterator_t protocolEnd = std::find(protocolStart, uriEnd, L':');            //"://");

    if (protocolEnd != uriEnd)
    {
        std::wstring prot = &*(protocolEnd);
        if ((prot.length() > 3) && (prot.substr(0, 3) == L"://"))
        {
            result.Protocol = std::wstring(protocolStart, protocolEnd);
            protocolEnd += 3;   //      ://
        }
        else
            protocolEnd = uri.begin();  // no protocol
    }
    else
        protocolEnd = uri.begin();  // no protocol

    // host
    iterator_t hostStart = protocolEnd;
    iterator_t pathStart = std::find(hostStart, uriEnd, L'/');  // get pathStart

    iterator_t hostEnd = std::find(protocolEnd, 
        (pathStart != uriEnd) ? pathStart : queryStart,
        L':');  // check for port

    result.Host = std::wstring(hostStart, hostEnd);

    // port
    if ((hostEnd != uriEnd) && ((&*(hostEnd))[0] == L':'))  // we have a port
    {
        hostEnd++;
        iterator_t portEnd = (pathStart != uriEnd) ? pathStart : queryStart;
        result.Port = std::wstring(hostEnd, portEnd);
    }

    // path
    if (pathStart != uriEnd)
        result.Path = std::wstring(pathStart, queryStart);

    // query
    if (queryStart != uriEnd)
        result.QueryString = std::wstring(queryStart, uri.end());

    return result;

}   // Parse
};  // uri

测试/使用

Uri u0 = Uri::Parse(L"http://localhost:80/foo.html?&q=1:2:3");
Uri u1 = Uri::Parse(L"https://localhost:80/foo.html?&q=1");
Uri u2 = Uri::Parse(L"localhost/foo");
Uri u3 = Uri::Parse(L"https://localhost/foo");
Uri u4 = Uri::Parse(L"localhost:8080");
Uri u5 = Uri::Parse(L"localhost?&foo=1");
Uri u6 = Uri::Parse(L"localhost?&foo=1:2:3");

u0.QueryString, u0.Path, u0.Protocol, u0.Host, u0.Port....

【讨论】:

  • 为什么要使用 wstring?
  • 我在互联网上爬网时发现的一件事是,现实世界中的 URL 通常被破坏或格式错误(但大多数浏览器仍然可以正确理解它们)。最大的一个是查询。是的,它应该以? 开头,在现实世界中它更经常以&amp; 开头。
  • 您的代码在ftp://user:passwd@example.com:1555/docs/Java&amp;C++ 上返回什么?
【解决方案3】:

非常抱歉,没办法。 :s

url.hh

#ifndef URL_HH_
#define URL_HH_    
#include <string>
struct url {
    url(const std::string& url_s); // omitted copy, ==, accessors, ...
private:
    void parse(const std::string& url_s);
private:
    std::string protocol_, host_, path_, query_;
};
#endif /* URL_HH_ */

url.cc

#include "url.hh"
#include <string>
#include <algorithm>
#include <cctype>
#include <functional>
using namespace std;

// ctors, copy, equality, ...

void url::parse(const string& url_s)
{
    const string prot_end("://");
    string::const_iterator prot_i = search(url_s.begin(), url_s.end(),
                                           prot_end.begin(), prot_end.end());
    protocol_.reserve(distance(url_s.begin(), prot_i));
    transform(url_s.begin(), prot_i,
              back_inserter(protocol_),
              ptr_fun<int,int>(tolower)); // protocol is icase
    if( prot_i == url_s.end() )
        return;
    advance(prot_i, prot_end.length());
    string::const_iterator path_i = find(prot_i, url_s.end(), '/');
    host_.reserve(distance(prot_i, path_i));
    transform(prot_i, path_i,
              back_inserter(host_),
              ptr_fun<int,int>(tolower)); // host is icase
    string::const_iterator query_i = find(path_i, url_s.end(), '?');
    path_.assign(path_i, query_i);
    if( query_i != url_s.end() )
        ++query_i;
    query_.assign(query_i, url_s.end());
}

main.cc

// ...
    url u("HTTP://stackoverflow.com/questions/2616011/parse-a.py?url=1");
    cout << u.protocol() << '\t' << u.host() << ...

【讨论】:

  • 小吹牛:你不需要在这里使用ptr_fun,如果你需要,你需要#include &lt;functional&gt;。 (你可能也不应该using namespace std,但我假设这不是用于生产代码)
  • 我省略了一些琐碎的功能,比如赋值运算符、构造函数、访问器等等。 url 类不应该有修改器。对于相等运算符,您可以添加一个在解析原始字符串时填写的哈希成员。然后,比较两个 url 是否相等应该非常快。这也意味着一些额外的复杂性;这是你的电话。
  • @Billy 我总是将命名空间std 带入我的编译单元(不是头文件!)。我认为这完全没问题,而且我认为到处都是 std:: 比引入命名空间造成更多的污染和眼睛疲劳。
  • 有趣的是,恰恰相反,我同意比利·奥尼尔的观点,并删除了我遇到的所有using namespace。如果你真的重复一个符号,你总是可以有using std::string;,但我更喜欢有命名空间限定,让可怜的老我更容易理解那个符号来自哪里。
  • 除了example.com:port/pathname,还有很多不支持的URI/URL形式。例如 http:/pathname 更重要的是 username:password@example.com/pathname#section - 所有组合都列在 ietf.org/rfc/rfc2396.txt 中 - 它们显示以下正则表达式:^(([^:/?#]+):)?(//([^ /?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
【解决方案4】:

POCO 的 URI 类可以为您解析 URL。以下示例是POCO URI and UUID slides 中的示例的简化版本:

#include "Poco/URI.h"
#include <iostream>

int main(int argc, char** argv)
{
    Poco::URI uri1("http://www.appinf.com:88/sample?example-query#frag");

    std::string scheme(uri1.getScheme()); // "http"
    std::string auth(uri1.getAuthority()); // "www.appinf.com:88"
    std::string host(uri1.getHost()); // "www.appinf.com"
    unsigned short port = uri1.getPort(); // 88
    std::string path(uri1.getPath()); // "/sample"
    std::string query(uri1.getQuery()); // "example-query"
    std::string frag(uri1.getFragment()); // "frag"
    std::string pathEtc(uri1.getPathEtc()); // "/sample?example-query#frag"

    return 0;
}

【讨论】:

    【解决方案5】:

    为了完整起见,您可以使用一个用 C 语言编写的代码(毫无疑问,需要稍微包装一下):https://uriparser.github.io/

    [符合 RFC 并支持 Unicode]


    这是一个非常基本的包装器,我一直在使用它来简单地获取解析结果。

    #include <string>
    #include <uriparser/Uri.h>
    
    
    namespace uriparser
    {
        class Uri //: boost::noncopyable
        {
            public:
                Uri(std::string uri)
                    : uri_(uri)
                {
                    UriParserStateA state_;
                    state_.uri = &uriParse_;
                    isValid_   = uriParseUriA(&state_, uri_.c_str()) == URI_SUCCESS;
                }
    
                ~Uri() { uriFreeUriMembersA(&uriParse_); }
    
                bool isValid() const { return isValid_; }
    
                std::string scheme()   const { return fromRange(uriParse_.scheme); }
                std::string host()     const { return fromRange(uriParse_.hostText); }
                std::string port()     const { return fromRange(uriParse_.portText); }
                std::string path()     const { return fromList(uriParse_.pathHead, "/"); }
                std::string query()    const { return fromRange(uriParse_.query); }
                std::string fragment() const { return fromRange(uriParse_.fragment); }
    
            private:
                std::string uri_;
                UriUriA     uriParse_;
                bool        isValid_;
    
                std::string fromRange(const UriTextRangeA & rng) const
                {
                    return std::string(rng.first, rng.afterLast);
                }
    
                std::string fromList(UriPathSegmentA * xs, const std::string & delim) const
                {
                    UriPathSegmentStructA * head(xs);
                    std::string accum;
    
                    while (head)
                    {
                        accum += delim + fromRange(head->text);
                        head = head->next;
                    }
    
                    return accum;
                }
        };
    }
    

    【讨论】:

    • +1,我最终从 github 克隆了你的 URL 解析器库。不用全力以赴就好多了……
    • @Alan 我不知道 Boost 中的 URL 解析器。 cpp-netlib 有一个,但我遇到了问题(现在很可能已修复)所以我改用这个。
    【解决方案6】:
    //sudo apt-get install libboost-all-dev; #install boost
    //g++ urlregex.cpp -lboost_regex; #compile
    #include <string>
    #include <iostream>
    #include <boost/regex.hpp>
    
    using namespace std;
    
    int main(int argc, char* argv[])
    {
        string url="https://www.google.com:443/webhp?gws_rd=ssl#q=cpp";
        boost::regex ex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
        boost::cmatch what;
        if(regex_match(url.c_str(), what, ex)) 
        {
            cout << "protocol: " << string(what[1].first, what[1].second) << endl;
            cout << "domain:   " << string(what[2].first, what[2].second) << endl;
            cout << "port:     " << string(what[3].first, what[3].second) << endl;
            cout << "path:     " << string(what[4].first, what[4].second) << endl;
            cout << "query:    " << string(what[5].first, what[5].second) << endl;
            cout << "fragment: " << string(what[6].first, what[6].second) << endl;
        }
        return 0;
    }
    

    【讨论】:

    • 运行良好但速度慢
    【解决方案7】:

    Poco 库现在有一个类用于解析 URI 并反馈主机、路径段和查询字符串等。

    https://pocoproject.org/pro/docs/Poco.URI.html

    【讨论】:

      【解决方案8】:

      Facebook 的Folly 库可以轻松为您完成这项工作。只需使用Uri 类:

      #include <folly/Uri.h>
      
      int main() {
          folly::Uri folly("https://code.facebook.com/posts/177011135812493/");
      
          folly.scheme(); // https
          folly.host();   // code.facebook.com
          folly.path();   // posts/177011135812493/
      }
      

      【讨论】:

        【解决方案9】:

        QT 为此提供了QUrl。 GNOME 在libsoup 中有SoupURI,您可能会发现它更轻量级。

        【讨论】:

          【解决方案10】:

          这个库非常小巧轻便:https://github.com/corporateshark/LUrlParser

          但是,它只是解析,没有 URL 规范化/验证。

          【讨论】:

            【解决方案11】:

            我知道这是一个非常古老的问题,但我发现以下内容很有用:

            http://www.zedwood.com/article/cpp-boost-url-regex

            它给出了 3 个例子:

            (带升压)

            //sudo apt-get install libboost-all-dev;
            //g++ urlregex.cpp -lboost_regex
            #include <string>
            #include <iostream>
            #include <boost/regex.hpp>
            
            using std::string;
            using std::cout;
            using std::endl;
            using std::stringstream;
            
            void parse_url(const string& url) //with boost
            {
                boost::regex ex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
                boost::cmatch what;
                if(regex_match(url.c_str(), what, ex)) 
                {
                    string protocol = string(what[1].first, what[1].second);
                    string domain   = string(what[2].first, what[2].second);
                    string port     = string(what[3].first, what[3].second);
                    string path     = string(what[4].first, what[4].second);
                    string query    = string(what[5].first, what[5].second);
                    cout << "[" << url << "]" << endl;
                    cout << protocol << endl;
                    cout << domain << endl;
                    cout << port << endl;
                    cout << path << endl;
                    cout << query << endl;
                    cout << "-------------------------------" << endl;
                }
            }
            
            int main(int argc, char* argv[])
            {
                parse_url("http://www.google.com");
                parse_url("https://mail.google.com/mail/");
                parse_url("https://www.google.com:443/webhp?gws_rd=ssl");
                return 0;
            }
            

            (无增强)

            #include <string>
            #include <iostream>
            
            using std::string;
            using std::cout;
            using std::endl;
            using std::stringstream;
            
            string _trim(const string& str)
            {
                size_t start = str.find_first_not_of(" \n\r\t");
                size_t until = str.find_last_not_of(" \n\r\t");
                string::const_iterator i = start==string::npos ? str.begin() : str.begin() + start;
                string::const_iterator x = until==string::npos ? str.end()   : str.begin() + until+1;
                return string(i,x);
            }
            
            void parse_url(const string& raw_url) //no boost
            {
                string path,domain,x,protocol,port,query;
                int offset = 0;
                size_t pos1,pos2,pos3,pos4;
                x = _trim(raw_url);
                offset = offset==0 && x.compare(0, 8, "https://")==0 ? 8 : offset;
                offset = offset==0 && x.compare(0, 7, "http://" )==0 ? 7 : offset;
                pos1 = x.find_first_of('/', offset+1 );
                path = pos1==string::npos ? "" : x.substr(pos1);
                domain = string( x.begin()+offset, pos1 != string::npos ? x.begin()+pos1 : x.end() );
                path = (pos2 = path.find("#"))!=string::npos ? path.substr(0,pos2) : path;
                port = (pos3 = domain.find(":"))!=string::npos ? domain.substr(pos3+1) : "";
                domain = domain.substr(0, pos3!=string::npos ? pos3 : domain.length());
                protocol = offset > 0 ? x.substr(0,offset-3) : "";
                query = (pos4 = path.find("?"))!=string::npos ? path.substr(pos4+1) : "";
                path = pos4!=string::npos ? path.substr(0,pos4) : path;
                cout << "[" << raw_url << "]" << endl;
                cout << "protocol: " << protocol << endl;
                cout << "domain: " << domain << endl;
                cout << "port: " << port << endl;
                cout << "path: " << path << endl;
                cout << "query: " << query << endl;
            }
            
            int main(int argc, char* argv[])
            {
                parse_url("http://www.google.com");
                parse_url("https://mail.google.com/mail/");
                parse_url("https://www.google.com:443/webhp?gws_rd=ssl");
                return 0;
            }
            

            (没有Boost的不同方式)

            #include <string>
            #include <stdint.h>
            #include <cstring>
            #include <sstream>
            #include <algorithm>
            
            #include <iostream> 
            using std::cerr; using std::cout; using std::endl;
            
            using std::string;
            
            class HTTPURL
            {
                private:
                    string _protocol;// http vs https
                    string _domain;  // mail.google.com
                    uint16_t _port;  // 80,443
                    string _path;    // /mail/
                    string _query;   // [after ?] a=b&c=b
            
                public:
                    const string &protocol;
                    const string &domain;
                    const uint16_t &port;
                    const string &path;
                    const string &query;
            
                    HTTPURL(const string& url): protocol(_protocol),domain(_domain),port(_port),path(_path),query(_query)
                    {
                        string u = _trim(url);
                        size_t offset=0, slash_pos, hash_pos, colon_pos, qmark_pos;
                        string urlpath,urldomain,urlport;
                        uint16_t default_port;
            
                        static const char* allowed[] = { "https://", "http://", "ftp://", NULL};
                        for(int i=0; allowed[i]!=NULL && this->_protocol.length()==0; i++)
                        {
                            const char* c=allowed[i];
                            if (u.compare(0,strlen(c), c)==0) {
                                offset = strlen(c);
                                this->_protocol=string(c,0,offset-3);
                            }
                        }
                        default_port = this->_protocol=="https" ? 443 : 80;
                        slash_pos = u.find_first_of('/', offset+1 );
                        urlpath = slash_pos==string::npos ? "/" : u.substr(slash_pos);
                        urldomain = string( u.begin()+offset, slash_pos != string::npos ? u.begin()+slash_pos : u.end() );
                        urlpath = (hash_pos = urlpath.find("#"))!=string::npos ? urlpath.substr(0,hash_pos) : urlpath;
                        urlport = (colon_pos = urldomain.find(":"))!=string::npos ? urldomain.substr(colon_pos+1) : "";
                        urldomain = urldomain.substr(0, colon_pos!=string::npos ? colon_pos : urldomain.length());
                        this->_domain = _tolower(urldomain);
                        this->_query = (qmark_pos = urlpath.find("?"))!=string::npos ? urlpath.substr(qmark_pos+1) : "";
                        this->_path = qmark_pos!=string::npos ? urlpath.substr(0,qmark_pos) : urlpath;
                        this->_port = urlport.length()==0 ? default_port : _atoi(urlport) ;
                    };
                private:
                    static inline string _trim(const string& input)
                    {
                        string str = input;
                        size_t endpos = str.find_last_not_of(" \t\n\r");
                        if( string::npos != endpos )
                        {
                            str = str.substr( 0, endpos+1 );
                        }
                        size_t startpos = str.find_first_not_of(" \t\n\r");
                        if( string::npos != startpos )
                        {
                            str = str.substr( startpos );
                        }
                        return str;
                    };
                    static inline string _tolower(const string& input)
                    {
                        string str = input;
                        std::transform(str.begin(), str.end(), str.begin(), ::tolower);
                        return str;
                    };
                    static inline int _atoi(const string& input) 
                    {
                        int r;
                        std::stringstream(input) >> r;
                        return r;
                    };
            };
            
            int main(int argc, char **argv)
            {
                HTTPURL u("https://Mail.google.com:80/mail/?action=send#action=send");
                cout << "protocol: " << u.protocol << endl;
                cout << "domain: " << u.domain << endl;
                cout << "port: " << u.port << endl;
                cout << "path: " << u.path << endl;
                cout << "query: " << u.query << endl;
                return 0;
            }
            

            【讨论】:

              【解决方案12】:

              http://code.google.com/p/uri-grammar/ 也很有趣,就像 Dean Michael 的 netlib 使用 boost 精神来解析 URI 一样。在Simple expression parser example using Boost::Spirit?看到它

              【讨论】:

                【解决方案13】:

                我能否提供另一种基于 std::regex 的独立解决方案:

                const char* SCHEME_REGEX   = "((http[s]?)://)?";  // match http or https before the ://
                const char* USER_REGEX     = "(([^@/:\\s]+)@)?";  // match anything other than @ / : or whitespace before the ending @
                const char* HOST_REGEX     = "([^@/:\\s]+)";      // mandatory. match anything other than @ / : or whitespace
                const char* PORT_REGEX     = "(:([0-9]{1,5}))?";  // after the : match 1 to 5 digits
                const char* PATH_REGEX     = "(/[^:#?\\s]*)?";    // after the / match anything other than : # ? or whitespace
                const char* QUERY_REGEX    = "(\\?(([^?;&#=]+=[^?;&#=]+)([;|&]([^?;&#=]+=[^?;&#=]+))*))?"; // after the ? match any number of x=y pairs, seperated by & or ;
                const char* FRAGMENT_REGEX = "(#([^#\\s]*))?";    // after the # match anything other than # or whitespace
                
                bool parseUri(const std::string &i_uri)
                {
                    static const std::regex regExpr(std::string("^")
                        + SCHEME_REGEX + USER_REGEX
                        + HOST_REGEX + PORT_REGEX
                        + PATH_REGEX + QUERY_REGEX
                        + FRAGMENT_REGEX + "$");
                
                    std::smatch matchResults;
                    if (std::regex_match(i_uri.cbegin(), i_uri.cend(), matchResults, regExpr))
                    {
                        m_scheme.assign(matchResults[2].first, matchResults[2].second);
                        m_user.assign(matchResults[4].first, matchResults[4].second);
                        m_host.assign(matchResults[5].first, matchResults[5].second);
                        m_port.assign(matchResults[7].first, matchResults[7].second);
                        m_path.assign(matchResults[8].first, matchResults[8].second);
                        m_query.assign(matchResults[10].first, matchResults[10].second);
                        m_fragment.assign(matchResults[15].first, matchResults[15].second);
                
                        return true;
                    }
                
                    return false;
                }
                

                我为正则表达式的每个部分添加了解释。通过这种方式,您可以准确地选择相关部分来解析您期望获得的 URL。请记住相应地更改所需的正则表达式组索引。

                【讨论】:

                  【解决方案14】:

                  您可以使用的一个小依赖项是 uriparser,它最近已移至 GitHub。

                  你可以在他们的代码中找到一个最小的例子:https://github.com/uriparser/uriparser/blob/63384be4fb8197264c55ff53a135110ecd5bd8c4/tool/uriparse.c

                  这将比 Boost 或 Poco 更轻量级。唯一的问题是它是 C。

                  还有一个Buckaroo包:

                  buckaroo add github.com/buckaroo-pm/uriparser
                  

                  【讨论】:

                    【解决方案15】:

                    我在这里尝试了几个解决方案,但后来决定编写自己的解决方案,可以将其放入没有任何外部依赖项的项目中(c++17 除外)。

                    现在,它通过了所有测试。但是,如果您发现任何不成功的案例,请随时创建拉取请求或问题。

                    我会及时更新它并提高它的质量。欢迎提出建议!我也在尝试这种设计,使每个存储库只有一个高质量的类,以便可以将标头和源代码放入项目中(而不是构建库或仅标头)。它似乎运行良好(我在自己的项目中使用 git 子模块和符号链接)。

                    https://github.com/homer6/url

                    【讨论】:

                      【解决方案16】:

                      有新发布的google-url lib:

                      http://code.google.com/p/google-url/

                      该库提供了一个低级 url 解析 API 以及一个称为 GURL 的高级抽象。这是一个使用它的示例:

                      #include <googleurl\src\gurl.h>
                      
                      wchar_t url[] = L"http://www.facebook.com";
                      GURL parsedUrl (url);
                      assert(parsedUrl.DomainIs("facebook.com"));
                      

                      我对它有两个小抱怨:(1) 它希望默认使用 ICU 来处理不同的字符串编码;(2) 它对日志记录做了一些假设(但我认为它们可以被禁用)。换句话说,这个库并不是完全独立的,但我认为它仍然是一个很好的起点,特别是如果你已经在使用 ICU。

                      【讨论】:

                      • 它与铬源合并,不再单独维护
                      【解决方案17】:

                      您可以尝试名为 C++ REST SDK 的开源库(由 Microsoft 创建,在 Apache License 2.0 下分发)。它可以为多个平台构建,包括 Windows、Linux、OSX、iOS、Android)。有一个名为web::uri 的类,您可以在其中输入一个字符串并检索各个 URL 组件。这是一个代码示例(在 Windows 上测试):

                      #include <cpprest/base_uri.h>
                      #include <iostream>
                      #include <ostream>
                      
                      web::uri sample_uri( L"http://dummyuser@localhost:7777/dummypath?dummyquery#dummyfragment" );
                      std::wcout << L"scheme: "   << sample_uri.scheme()     << std::endl;
                      std::wcout << L"user: "     << sample_uri.user_info()  << std::endl;
                      std::wcout << L"host: "     << sample_uri.host()       << std::endl;
                      std::wcout << L"port: "     << sample_uri.port()       << std::endl;
                      std::wcout << L"path: "     << sample_uri.path()       << std::endl;
                      std::wcout << L"query: "    << sample_uri.query()      << std::endl;
                      std::wcout << L"fragment: " << sample_uri.fragment()   << std::endl;
                      

                      输出将是:

                      scheme: http
                      user: dummyuser
                      host: localhost
                      port: 7777
                      path: /dummypath
                      query: dummyquery
                      fragment: dummyfragment
                      

                      还有其他易于使用的方法,例如从查询中访问单个属性/值对,将路径拆分为组件等。

                      【讨论】:

                        【解决方案18】:

                        如果您使用oatpp 进行网络请求处理,您会发现其内置的 URL 解析很有用:

                          std::string url = /* ... */;
                          oatpp::String oatUrl(url.c_str(), url.size(), false);
                          oatpp::String oatHost = oatpp::network::Url::Parser::parseUrl(oatUrl).authority.host->toLowerCase();
                          std::string host(oatHost->c_str(), oatHost->getSize());
                        

                        上面的 sn -p 检索主机名。以类似的方式:

                        oatpp::network::Url parsedUrl = oatpp::network::Url::Parser::parseUrl(oatUrl);
                        // parsedUrl.authority.port
                        // parsedUrl.path
                        // parsedUrl.scheme
                        // parsedUrl.queryParams
                        

                        【讨论】:

                          【解决方案19】:

                          还有另一个库 https://snapwebsites.org/project/libtld 处理所有可能的顶级域和 URI shema

                          【讨论】:

                            【解决方案20】:

                            我开发了一个“面向对象”的解决方案,一个 C++ 类,它与一个正则表达式一起工作,比如@Mr.Jones 和@velcrow 解决方案。我的Url 类执行 url/uri '解析'。

                            我认为我改进了 velcrow 正则表达式,使其更加健壮,并且还包括用户名部分。

                            按照我的想法的第一个版本,我在我的 GPL3 许可开源项目Cpp URL Parser 中发布了相同的代码,经过改进。

                            省略#ifdef/ndef膨胀部分,紧跟Url.h

                            #include <string>
                            #include <iostream>
                            #include <boost/regex.hpp>
                            
                            using namespace std;
                            
                            class Url {
                            public:
                                boost::regex ex;
                                string rawUrl;
                            
                                string username;
                                string protocol;
                                string domain;
                                string port;
                                string path;
                                string query;
                                string fragment;
                            
                                Url();
                            
                                Url(string &rawUrl);
                            
                                Url &update(string &rawUrl);
                            };
                            

                            这是Url.cpp实现文件的代码:

                            #include "Url.h"
                            
                            Url::Url() {
                                this -> ex = boost::regex("(ssh|sftp|ftp|smb|http|https):\\/\\/(?:([^@ ]*)@)?([^:?# ]+)(?::(\\d+))?([^?# ]*)(?:\\?([^# ]*))?(?:#([^ ]*))?");
                            }
                            
                            Url::Url(string &rawUrl) : Url() {
                                this->rawUrl = rawUrl;
                                this->update(this->rawUrl);
                            }
                            
                            Url &Url::update(string &rawUrl) {
                                this->rawUrl = rawUrl;
                                boost::cmatch what;
                                if (regex_match(rawUrl.c_str(), what, ex)) {
                                    this -> protocol = string(what[1].first, what[1].second);
                                    this -> username = string(what[2].first, what[2].second);
                                    this -> domain = string(what[3].first, what[3].second);
                                    this -> port = string(what[4].first, what[4].second);
                                    this -> path = string(what[5].first, what[5].second);
                                    this -> query = string(what[6].first, what[6].second);
                                    this -> fragment = string(what[7].first, what[7].second);
                                }
                                return *this;
                            }
                            

                            使用示例:

                            string urlString = "http://gino@ciao.it:67/ciao?roba=ciao#34";
                            Url *url = new Url(urlString);
                            std::cout << " username: " << url->username << " URL domain: " << url->domain;
                            std::cout << " port: " << url->port << " protocol: " << url->protocol;
                            

                            您还可以更新 Url 对象以表示(和解析)另一个 URL:

                            url.update("http://gino@nuovociao.it:68/nuovociao?roba=ciaoooo#")
                            

                            我不是全职 C++ 开发人员,因此,我不确定我是否遵循了 100% C++ 最佳实践。 任何提示表示赞赏。

                            P.s:让我们看看Cpp URL Parser,那里有改进。

                            玩得开心

                            【讨论】:

                              【解决方案21】:

                              获取协议、主机、路径的简单解决方案

                              int url_get(const std::string& uri)
                              {
                                //parse URI
                                std::size_t start = uri.find("://", 0);
                                if (start == std::string::npos)
                                {
                                  return -1;
                                }
                                start += 3; //"://"
                                std::size_t end = uri.find("/", start + 1);
                                std::string protocol = uri.substr(0, start - 3);
                                std::string host = uri.substr(start, end - start);
                                std::string path = uri.substr(end);
                                return 0;
                              }
                              
                              

                              【讨论】:

                              • 你的代码在host from ftp://user:passwd@example.com:1555/docs/Java&amp;C++ 中返回什么?
                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2011-10-18
                              • 2011-06-14
                              • 1970-01-01
                              • 1970-01-01
                              • 2012-05-21
                              相关资源
                              最近更新 更多