Ich muss eine URL parsen, um das Protokoll, den Host, den Pfad und die Abfrage in einer Anwendung zu erhalten, die ich in C++ schreibe. Die Anwendung soll plattformübergreifend sein. Ich bin überrascht, dass ich nichts finden kann, was dies in der erhöhen ou POCO Bibliotheken. Ist es irgendwo offensichtlich, dass ich nicht danach suche? Gibt es Vorschläge für geeignete Open-Source-Bibliotheken? Oder ist dies etwas, das ich einfach selbst machen muss? Es ist nicht super kompliziert, aber es scheint, wie eine solche gemeinsame Aufgabe, die ich bin überrascht, es ist nicht eine gemeinsame Lösung.
Antworten
Zu viele Anzeigen?Es gibt eine Bibliothek, die für die Einbindung in Boost vorgeschlagen wird und mit der Sie HTTP-URIs einfach parsen können. Sie verwendet Boost.Spirit und ist auch unter der Boost Software License veröffentlicht. Die Bibliothek heißt cpp-netlib. Die Dokumentation dazu finden Sie unter http://cpp-netlib.github.com/ -- Sie können die neueste Version herunterladen unter http://github.com/cpp-netlib/cpp-netlib/downloads .
Der entsprechende Typ, den Sie verwenden sollten, ist boost::network::http::uri
und ist dokumentiert aquí .
Wstring-Version von oben, mit weiteren benötigten Feldern. Könnte definitiv verfeinert werden, aber gut genug für meine Zwecke.
#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
Tests/Verwendung
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....
Tut mir schrecklich leid, ich konnte nicht anders :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() << ...
POCOs URI-Klasse kann URLs für Sie parsen. Das folgende Beispiel ist eine verkürzte Version des Beispiels in POCO URI und UUID Folien :
#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;
}
Der Vollständigkeit halber sei erwähnt, dass es ein in C geschriebenes Programm gibt, das Sie verwenden könnten (mit ein wenig Wrapping, kein Zweifel): https://uriparser.github.io/
[RFC-konform und unterstützt Unicode]
Hier ist ein sehr einfacher Wrapper, den ich verwendet habe, um einfach die Ergebnisse eines Parses abzurufen.
#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;
}
};
}
- See previous answers
- Weitere Antworten anzeigen
2 Stimmen
C++ (und erst recht C) ist nicht wie einige andere Sprachen. Es ist nicht so, dass Standardbibliotheken für alles Mögliche standardmäßig vorhanden sind. Es mag zwar einige allgemein gebräuchliche Bibliotheken geben, aber im Hinblick auf Standardbibliotheken, Sprachfunktionen und sogar betriebssystemspezifische APIs wie POSIX wird davon ausgegangen, dass man vieles selbst machen kann.
42 Stimmen
Ich baue gerne ein Rad, aber ich sehe keinen Sinn darin, es zu bauen, wenn es schon jemand anderes getan hat. Daher meine Frage. Sie haben Recht, "Es könnte einige Bibliothek in der gemeinsamen Nutzung sein" - das ist, was ich gefragt habe.
1 Stimmen
Es ist die Art von kleinem Dienstprogramm, das Sie in dem großen Framework finden würden, auf das sich Ihre Codebasis stützt. Wenn es nicht da ist, dann ist es eine lustige Übung in Standard-Algorithmen, um eine kleine URL-Dienstprogramm-Sammlung zu schreiben.
1 Stimmen
curl.haxx.se/libcurl/c/curl_unescape.html
0 Stimmen
Zum Parsen von URLs mit der
RFC 3986
einfach und ohne neue Bibliotheken zu importieren, finden Sie in dieser Antwort auf eine ähnliche Frage: stackoverflow.com/a/31613265/1043704