ProgramingTip

C ++ 크로스 플랫폼에서 URL을 파싱하는 쉬운 방법?

bestdevel 2020. 11. 11. 20:30
반응형

C ++ 크로스 플랫폼에서 URL을 파싱하는 쉬운 방법?


C ++로 작성중인 응용 프로그램에서 프로토콜, 호스트, 경로 및 쿼리를 가져 오려면 URL을 구문 분석해야합니다. 이 응용 프로그램은 크로스 플랫폼 용입니다. 부스트 또는 POCO를 수행 하는 것을 수행하는 라이브러리가 있습니다. 내가 보지 않는 명백한 곳입니까? 적절한 제안 제안이? 아니면 내가 스스로해야 할 일인가? 매우 복잡하지만 공통적 인 것입니다.


Boost 포함을 위해 제안 된 라이브러리가 제공되는 HTTP URI를 쉽게 구문 분석 할 수 있습니다. Boost.Spirit을 사용하며 Boost 소프트웨어 라이선스에 따라 릴리스됩니다. 라이브러리는 http://cpp-netlib.github.com/ 에서 문서를 사용할 수있는 cpp-netlib입니다. http://github.com/cpp-netlib/cpp-netlib 에서 최신 릴리스를 다운로드 할 수 있습니다. / 다운로드 .

사용하려는 관련 유형 boost::network::http::uri여기 에 설명되어 있습니다 .


대단히 죄송합니다. 어쩔 수 없었습니다. : 에스

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

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

(의심의 여지없이 약간의 랩핑 포함) : http://uriparser.sourceforge.net/

[RFC 호환 및 유니 코드 지원]


다음은 포함 된 구문 분석 결과를 가져 오는 데 사용되는 매우 기본적인 래퍼입니다.

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

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

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

Poco 라이브러리에는 이제 URI를 분석하고 호스트, 경로 세그먼트 및 쿼리 등을 사용하는 클래스가 있습니다.

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


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

또한 흥미로운 것은 http://code.google.com/p/uri-grammar/ 인데, Dean Michael의 netlib처럼 부스트 스피릿을 사용하여 URI를 구문 분석합니다. Boost :: Spirit을 사용하는 간단한 요약 파서 예제 에서 보셨나요?


이 라이브러리는 매우 작고 가볍습니다 : https://github.com/corporateshark/LUrlParser

그러나 파싱 전용이며 URL 정규화 / 유효성 검사는 없습니다.


사용할 수있는 작은 정보는 최근 GitHub로 이동 한 uriparser 입니다.

코드에서 최소한의 예제를 사용할 수 있습니다 : https://github.com/uriparser/uriparser/blob/63384be4fb8197264c55ff53a135110ecd5bd8c4/tool/uriparse.c

이것은 Boost 또는 Poco보다 더 가볍습니다. 유일한 문제는 C라는 것입니다.

도있다 Buckaroo의 패키지 :

buckaroo add github.com/buckaroo-pm/uriparser

QT에는 이를 위한 QUrl 이 있습니다. 그놈은 libsoupSoupURI를 가지고 있는데 , 아마도 조금 더 가볍다는 것을 알게 될 것입니다 .


새로 출시 된 google-url lib가 있습니다.

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

라이브러리는 GURL이라는 상위 수준 수준 높은 수준의 URL 구문 분석 API를 제공합니다. 즉, 예는 다음과 가능합니다.

#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를 이미 사용하고있는 경우에는 여전히 시작 좋은 기초 생각합니다.


C ++ REST SDK (Apache License 2.0에 따라 배포 된 Microsoft에서 생성) 라는 오픈 소스 라이브러리를 볼 수 있습니다. Windows, Linux, OSX, iOS, Android가 포함 된 여러 플랫폼 용으로 구축 할 수 있습니다. web::uriURL을 입력하고 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

쿼리에서 속성 식별 / 값 쌍에 액세스하고 경로를 구성 요소로 분할하는 등 사용하기 쉬운 다른 방법도 있습니다.


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을 구문 분석 할 관련 부분을 정확하게 선택할 수 있습니다. 그에 따라 원하는 정규식 그룹 인덱스를 변경하는 것을 잊지 마십시오.


가능한 모든 최상위 도메인과 URI shema를 처리하는 또 다른 라이브러리 https://snapwebsites.org/project/libtld 가 있습니다.


@ Mr.Jones 및 @velcrow 솔루션과 같은 하나의 정규식과 함께 작동하는 하나의 C ++ 클래스 인 "객체 지향"솔루션을 개발했습니다. Url수업은 url / uri '파싱'을 수행합니다.

velcrow 정규식을 더 강력하게 개선 하고 사용자 이름 부분도 포함 한다고 생각 합니다.

내 아이디어의 첫 번째 버전을 따르고 GPL3 라이센스 오픈 소스 프로젝트 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구현 파일 의 코드입니다 .

#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://gino@ciao.it: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://gino@nuovociao.it:68/nuovociao?roba=ciaoooo#")

지금은 C ++를 배우고 있으므로 100 % C ++ 모범 사례를 따랐는지 잘 모르겠습니다. 모든 팁을 주시면 감사하겠습니다.

추신 : Cpp URL Parser를 살펴 보겠습니다 . 여기에 개선 사항이 있습니다.

재미있게

참고 URL : https://stackoverflow.com/questions/2616011/easy-way-to-parse-a-url-in-c-cross-platform

반응형