Подтвердить что ты не робот

Легкий способ разобрать URL-адрес на кросс-платформе С++?

Мне нужно проанализировать URL-адрес, чтобы получить протокол, хост, путь и запрос в приложении, которое я пишу на С++. Приложение предназначено для кросс-платформенной работы. Я удивлен, что не могу найти ничего, что делает это в boost или POCO. Это где-то очевидно, я не смотрю? Любые предложения относительно соответствующих библиотек с открытым исходным кодом? Или это то, что я просто должен делать сам? Это не слишком сложно, но, похоже, такая общая задача, я удивлен, что нет общего решения.

4b9b3361

Ответ 1

Существует библиотека, которая предлагается для включения Boost и позволяет легко анализировать HTTP URI. Он использует Boost.Spirit и также выпущен под лицензией Boost Software. Библиотека - это cpp-netlib, которую вы можете найти в документации http://cpp-netlib.github.com/ - вы можете загрузить последнюю версию из http://github.com/cpp-netlib/cpp-netlib/downloads.

Соответствующий тип, который вы хотите использовать, boost::network::http::uri и документально подтвержден здесь.

Ответ 2

Тяжело жаль, не смог.: 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() << ...

Ответ 3

Версия 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....

Ответ 4

Для полноты есть один написанный на 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;
            }
    };
}

Ответ 5

Класс POCO URI может анализировать URL-адреса для вас. Следующий пример - сокращенная версия версии в POCO URI и слайдах UUID:

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

Ответ 6

В библиотеке Poco теперь есть класс для анализа URI и обратной связи хоста, сегментов пути, строки запроса и т.д.

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

Ответ 7

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

Ответ 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/
}

Ответ 10

QT имеет QUrl для этого. GNOME имеет SoupURI в libsoup, которые вы, вероятно, найдете немного более легкими.

Ответ 11

Существует недавно выпущенный google-url lib:

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

Библиотека предоставляет низкоуровневый API-интерфейс разбора URL-адресов, а также абстракцию более высокого уровня под названием 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.

Ответ 12

Эта библиотека очень крошечная и легкая: https://github.com/corporateshark/LUrlParser

Однако он обрабатывает только, не имеет нормализации/подтверждения URL.

Ответ 13

Вы можете попробовать библиотеку с открытым исходным кодом под названием С++ 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://[email protected]: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

Существуют также другие простые в использовании методы, например. для доступа к отдельным парам атрибутов/значений из запроса, разбиению пути на компоненты и т.д.

Ответ 14

Я тоже искал простую автономную библиотеку URI для С++. Не имея возможности найти один, я взял URI-класс из Poco, рекомендованный в этой теме, и сделал его независимым, сделав несколько изменений в исходных исходных файлах. Сделано из 2 исходных файлов и не требует каких-либо exgernal-библиотек, использует только несколько заголовков из STL. Я провел некоторое тестирование с помощью компиляторов GCC и MS и разместил его на своем веб-сайте: http://ikk.byethost9.com/index.php?MainMenu=hef_uri_syntax Он переименовал пространство имен Poco → hef и переименовал основной URI класса → HfURISyntax. Его enncoured, чтобы переименовать их при использовании в ваших собственных проектах. (Исходное авторское право включено. Существует текстовый документ, содержащий сводку изменений.)

Ответ 15

Существует еще одна библиотека https://snapwebsites.org/project/libtld, которая обрабатывает все возможные домены верхнего уровня и URI shema

Ответ 16

Могу ли я предложить другое автономное решение на основе 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, который вы ожидаете получить. Просто не забудьте изменить нужные индексы группы регулярных выражений соответственно.

Ответ 17

Я разработал "объектно-ориентированное" решение, один класс C++, который работает с одним регулярным выражением, таким как решения @Mr.Jones и @velcrow. Мой класс Url выполняет разбор url/uri.

Я думаю, что я улучшил регулярное выражение Velcrow, чтобы сделать его более надежным и включил также часть имени пользователя.

Следуя первой версии моей идеи, я выпустил такой же улучшенный код в моем лицензированном GPL3-проекте с открытым исходным кодом Cpp URL Parser.

#ifdef/ndef часть #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.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://[email protected]: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://[email protected]:68/nuovociao?roba=ciaoooo#")

Я изучаю C++ только сейчас, поэтому я не уверен, что следовал 100% C++ лучшим практикам. Любой совет приветствуется.

PS: давайте посмотрим на Cpp URL Parser, там есть уточнения.

Повеселись