84 Stimmen

Einfache Möglichkeit, eine Url in C++ plattformübergreifend zu parsen?

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.

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.

33voto

Dean Michael Punkte 3376

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í .

26voto

Tom Punkte 1878

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

24voto

wilhelmtell Punkte 55189

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

14voto

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

13voto

Elliot Cameron Punkte 5057

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X