Простой способ проанализировать url-адрес в кросс-платформе C++?
Мне нужно проанализировать URL-адрес, чтобы получить протокол, хост, путь и запрос в приложении, которое я пишу на C++. Приложение предназначено для кросс-платформенной работы. Я удивлен, что не могу найти ничего, что делает это в boost или POCO библиотеки. Это где-то очевидно, что я не смотрю? Любые предложения по соответствующим библиотекам с открытым исходным кодом? Или это то, что я просто должен сделать сам? Это не очень сложно, но кажется, что такая общая задача меня удивляет нет общего решения.
14 ответов:
существует библиотека, которая предлагается для включения Boost и позволяет легко анализировать HTTP URI. Он использует импульс.Дух и также выпущен под лицензией программного обеспечения Boost. Библиотека cpp-netlib, которую вы можете найти в документации по адресу http://cpp-netlib.github.com/ -- Вы можете скачать последнюю версию с http://github.com/cpp-netlib/cpp-netlib/downloads .
соответствующий тип вы хотите использовать
boost::network::http::uri
и документально здесь.
ужасно жаль, ничего не мог поделать. : s
url.чч
#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() << ...
версия 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....
для полноты есть один написанный на 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; } }; }
класс URI POCO может анализировать 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; }
библиотека Poco теперь имеет класс для разбиения URI и подачи обратно хоста, сегментов пути и строки запроса и т. д.
Facebook глупость библиотека может сделать работу за вас легко. Просто используйте 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/ }
//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; }
также интерес может быть http://code.google.com/p/uri-grammar/ который, как и netlib Дина Майкла, использует boost spirit для разбора URI. Наткнулся на него в простой пример парсера выражений с помощью Boost:: Spirit?
есть недавно выпущенный 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.
эта библиотека очень маленькая и легкая:https://github.com/corporateshark/LUrlParser
однако, это только разбор, нет нормализации/проверки URL.
вы можете попробовать библиотеку с открытым исходным кодом под названием C++ REST SDK (создано Microsoft, распространяется под лицензией Apache 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
есть и другие простые в использовании методов, например, чтобы получить доступ к отдельным парам атрибутов / значений из запроса, разделить путь на компоненты и т. д.
есть еще одна библиотека https://snapwebsites.org/project/libtld который обрабатывает все возможные домены верхнего уровня и URI shema