在C ++跨平台上解析url的简单方法?

问题描述 投票:63回答:17

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

c++ url uri
17个回答
28
投票

有一个建议用于包含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


2
投票

这个库非常小巧轻便:http://code.google.com/p/uri-grammar/

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


1
投票

1
投票

有新发布的google-url lib:

SoupURI

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

libsoup

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


1
投票

您可以尝试名为http://code.google.com/p/google-url/的开源库(由Microsoft创建,在Apache License 2.0下分发)。它可以为几个平台构建,包括Windows,Linux,OSX,iOS,Android)。有一个名为#include <googleurl\src\gurl.h> wchar_t url[] = L"http://www.facebook.com"; GURL parsedUrl (url); assert(parsedUrl.DomainIs("facebook.com")); 的类,您可以在其中放入一个字符串,并可以检索单个URL组件。这是一个代码示例(在Windows上测试):

C++ REST SDK

输出将是:

web::uri

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


1
投票

我可以提供另一个基于std :: regex的自包含解决方案:

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

我为正则表达式的每个部分添加了解释。这种方式允许您准确选择要解析的URL以获取您期望获得的URL。只需记住相应地更改所需的正则表达式组索引。


1
投票

你可以使用的一个小依赖是scheme: http user: dummyuser host: localhost port: 7777 path: /dummypath query: dummyquery fragment: dummyfragment ,它最近搬到了GitHub。

你可以在他们的代码中找到一个最小的例子: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; }

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

还有一个uriparser包:

https://github.com/uriparser/uriparser/blob/63384be4fb8197264c55ff53a135110ecd5bd8c4/tool/uriparse.c

0
投票

还有另一个库Buckaroo,它处理所有可能的顶级域名和URI shema


0
投票

我开发了一个“面向对象”的解决方案,一个C ++类,可以与@JonesJones和@velcrow解决方案等一个正则表达式一起使用。我的buckaroo add github.com/buckaroo-pm/uriparser 类执行url / uri'解析'。

我认为我改进了velcrow正则表达式更强大,还包括用户名部分。

按照我的想法的第一个版本,我在我的GPL3许可开源项目https://snapwebsites.org/project/libtld中发布了相同的代码,进行了改进。

省略Url膨胀部分,跟随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

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

#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;
}

我刚刚学习C ++,所以,我不确定我是否遵循100%C ++最佳实践。任何提示都表示赞赏。

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

玩得开心


20
投票

非常抱歉,忍不住了。 :■

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() << ...

19
投票

上面的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....

12
投票

为了完整性,有一个用C语言写的,你可以使用(有点包装,毫无疑问):http://uriparser.sourceforge.net/

[符合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;
            }
    };
}

8
投票

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
投票
//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;
}

5
投票

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

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


4
投票

Facebook qazxsw poi图书馆可以轻松完成这项工作。只需使用qazxsw poi类:

Folly

2
投票

同样感兴趣的可能是Uri,就像Dean Michael的netlib使用boost精神来解析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/ } 遇到它

© www.soinside.com 2019 - 2024. All rights reserved.