Jak sprawdzić, czy C ++ std :: string zaczyna się od określonego ciągu i przekonwertować podłańcuch na int?


242

Jak zaimplementować następujące (pseudokod Python) w C ++?

if argv[1].startswith('--foo='):
    foo_value = int(argv[1][len('--foo='):])

(Na przykład, jeśli argv[1]jest --foo=98, to foo_valuejest 98.)

Aktualizacja: waham się, czy zagłębić się w Boost, ponieważ chcę tylko dokonać niewielkiej zmiany w prostym, małym narzędziu wiersza poleceń (wolałbym nie uczyć się, jak łączyć się i używać Boost dla drobnej zmiana).


To też jest interesujące.
manlio

Odpowiedzi:


447

Użyj przeciążenia, rfindktórego posparametr ma parametr:

std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
  // s starts with prefix
}

Kto potrzebuje czegoś jeszcze? Pure STL!

Wielu błędnie odczytało to, co oznacza „przeszukuj wstecz cały ciąg szukając prefiksu”. To dałoby zły wynik (np. string("tititito").rfind("titi")Zwraca 2, więc w porównaniu z == 0zwróciłoby fałsz) i byłoby nieefektywne (przeglądanie całego łańcucha zamiast samego początku). Ale nie robi tego, ponieważ przekazuje posparametr as 0, co ogranicza wyszukiwanie do dopasowania tylko w tej pozycji lub wcześniej . Na przykład:

std::string test = "0123123";
size_t match1 = test.rfind("123");    // returns 4 (rightmost match)
size_t match2 = test.rfind("123", 2); // returns 1 (skipped over later match)
size_t match3 = test.rfind("123", 0); // returns std::string::npos (i.e. not found)

32
ta odpowiedź powinna być najbardziej pozytywna, a nie wzmocniona: D po co korzystać z innej biblioteki, gdy już masz STL.
Iuliu Atudosiei

@ sweisgerber.dev, jestem zdezorientowany co do pierwszego sporu. Zwracana wartość z findbędzie wynosić tylko zero, jeśli titijest na początku łańcucha. Jeśli zostanie znaleziony gdzie indziej, otrzymasz niezerową wartość zwracaną, a jeśli nie zostanie znaleziony, otrzymasz nposrównież niezerową. Zakładając, że mam rację, wolałbym tę odpowiedź, ponieważ nie muszę wnosić żadnych niestandardowych rzeczy (tak, wiem, że Boost jest wszędzie, po prostu wolę podstawowe biblioteki C ++ do takich prostych rzeczy).
paxdiablo

@paxdiablo: masz rację, rzeczywiście sprawdza, czy zaczyna się od titi, ale brakuje części konwersji.
sweisgerber.dev

2
Czy mamy jakieś dowody na to, że jest to zoptymalizowane w większości kompilatorów? Nigdzie indziej nie wspominam, że optymalizacja „znajdź” lub „znajdź” jest powszechną praktyką opartą na wartości zwracanej, z którą sprawdza.
Superziyi

2
@alcoforado „rfind rozpocznie się od tyłu łańcucha ...” Nie, dotyczy to tylko przeciążenia, rfind()które nie przyjmuje posparametru. Jeśli użyjesz przeciążenia, które pobiera posparametr, nie będzie przeszukiwał całego łańcucha, tylko tę pozycję i wcześniej. (Tak jak zwykły find()z posparametrem wygląda tylko w tej pozycji lub później.) Więc jeśli zdasz pos == 0, jak pokazano w tej odpowiedzi, to dosłownie rozważy tylko dopasowania dla tej jednej pozycji. To już wyjaśniało zarówno odpowiedź, jak i komentarze.
Arthur Tacca

188

Zrobiłbyś to w ten sposób:

std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
    foo_value = atoi(arg.substr(prefix.size()).c_str());

Poszukiwanie biblioteki takiej jak Boost.ProgramOptions, która zrobi to za Ciebie, jest również dobrym pomysłem.


7
Największym problemem jest to, że atoi("123xyz")zwraca 123, podczas gdy Python int("123xyz")zgłasza wyjątek.
Tom

Obejściem, które możemy zrobić, jest sscanf () i porównanie wyniku i oryginału, aby zdecydować, czy kontynuować, czy zgłosić wyjątek.
Roopesh Majeti,

1
Lub po prostu wymienić atoisię strtollub strtoll, co pozwala nam wykryć warunki błędach w wartości wejściowej.
Tom

1
To lepsze rozwiązanie niż to, rfindktóre zależy od optymalizacji do pracy.
Calmarius

143

Dla kompletności wspomnę o sposobie C:

Jeśli strjest to oryginalny ciąg, substrto podciąg, który chcesz sprawdzić, to

strncmp(str, substr, strlen(substr))

zwróci 0jeślistr zacznie się od substr. Funkcje strncmpi strlenznajdują się w pliku nagłówka C.<string.h>

(pierwotnie opublikowane przez Yaseen Rauf tutaj , dodano znaczniki)

Dla porównania bez rozróżniania wielkości liter, użyj strnicmpzamiast strncmp.

Jest to sposób C, aby to zrobić, dla ciągów C ++ możesz użyć tej samej funkcji, jak poniżej:

strncmp(str.c_str(), substr.c_str(), substr.size())

9
w rzeczy samej, wszyscy wydają się po prostu „używać boost”, a ja jestem wdzięczny za wersję biblioteki STL lub OS
Force Gaia

Tak. Zakłada jednak, że łańcuch nie zawiera znaków zerowych. Jeśli tak nie jest - należy użyćmemcmp()
Avishai Y

dlaczego ktoś miałby używać czegoś innego niż to proste, piękne rozwiązanie?
Adam Zahran

88

Jeśli już używasz wzmocnienia, możesz to zrobić za pomocą algorytmów ciągu wzmocnionego + wzmocnienia rzutowania leksykalnego:

#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>

try {    
    if (boost::starts_with(argv[1], "--foo="))
        foo_value = boost::lexical_cast<int>(argv[1]+6);
} catch (boost::bad_lexical_cast) {
    // bad parameter
}

Takie podejście, podobnie jak wiele innych podanych tutaj odpowiedzi, jest odpowiednie w przypadku bardzo prostych zadań, ale na dłuższą metę zwykle lepiej jest korzystać z biblioteki parsowania wiersza poleceń. Boost ma jeden ( Boost.Program_options ), co może mieć sens, jeśli już używasz .

W przeciwnym razie poszukiwanie „parsera wiersza poleceń c ++” da szereg opcji.


107
Wciąganie ogromnych zależności do sprawdzania prefiksu łańcucha jest jak strzelanie do ptaków z kanonami.
Tobi

150
„Użyj wzmocnienia” jest zawsze złą odpowiedzią, gdy ktoś pyta, jak wykonać prostą operację na łańcuchu w C ++.
Glenn Maynard,

90
minus 1 za sugerowanie wzmocnienia
uglycoyote

37
Używanie wzmocnienia tutaj jest słuszne, jeśli już używasz wzmocnienia w swoim projekcie.
Alex Che

17
Odpowiedź jest poprzedzona słowem „Jeśli używasz wzmocnienia ...”. Oczywiście jest to właściwa odpowiedź „... jeśli używasz wzmocnienia”. Jeśli nie, spójrz na sugestię @Thomas
NuSkooler

82

Kod, którego używam:

std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
    std::string argumentValue = argument.substr(prefix.size());
}

2
najbardziej zwięzły i zależy tylko od std :: string, z wyjątkiem usunięcia opcjonalnego i mylącego argument.size () na końcu końcowego substr.
Ben Bryant

@ ben-bryant: Dzięki za informacje. Nie wiedziałem, że to opcjonalne.
Hüseyin Yağlı

16
Korzystanie substrprowadzi do niepotrzebnego kopiowania. str.compare(start, count, substr)Metoda stosowana w odpowiedzi Thomas jest bardziej efektywny. Odpowiedź razvanco13 ma inną metodę, która pozwala uniknąć kopiowania za pomocą std::equal.
Felix Dombek

4
@ HüseyinYağlı Thomas uses atoi which is only for windowsHuh? atoijest standardową funkcją biblioteki C od ... kiedykolwiek. W rzeczywistości, atoijest złe-nie dlatego, że jest Windows specific- ale dlatego, że (1) C, nie C ++, oraz (2) przestarzałe nawet w C (należy używać strtollub jednego z innych, powiązanych funkcji. Ponieważ atoima brak obsługi błędów. Ale i tak to tylko w C).
Parthian Shot

50

Nikt jeszcze nie używał funkcji algorytmu / niedopasowania STL . Jeśli to zwraca wartość true, prefiks jest prefiksem „toCheck”:

std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()

Pełny przykładowy program:

#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, char** argv) {
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "Will print true if 'prefix' is a prefix of string" << std::endl;
        return -1;
    }
    std::string prefix(argv[1]);
    std::string toCheck(argv[2]);
    if (prefix.length() > toCheck.length()) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "'prefix' is longer than 'string'" <<  std::endl;
        return 2;
    }
    if (std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()) {
        std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck << '"' << std::endl;
        return 0;
    } else {
        std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"' << toCheck << '"' << std::endl;
        return 1;
    }
}

Edytować:

Jak sugeruje @James T. Huggett, std :: równe lepiej pasuje do pytania: czy A jest prefiksem B? i jest nieco krótszy kod:

std::equal(prefix.begin(), prefix.end(), toCheck.begin())

Pełny przykładowy program:

#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, char **argv) {
  if (argc != 3) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "Will print true if 'prefix' is a prefix of string"
              << std::endl;
    return -1;
  }
  std::string prefix(argv[1]);
  std::string toCheck(argv[2]);
  if (prefix.length() > toCheck.length()) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "'prefix' is longer than 'string'" << std::endl;
    return 2;
  }
  if (std::equal(prefix.begin(), prefix.end(), toCheck.begin())) {
    std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck
              << '"' << std::endl;
    return 0;
  } else {
    std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"'
              << toCheck << '"' << std::endl;
    return 1;
  }
}

2
Dlaczego nie użyć std :: równo?
Brice M. Dempsey

Dla mnie brzmi dobrze. Byłby to również krótszy kod. Spose, będę musiał edytować odpowiedź teraz: p
matiu

2
Używanie std::equalciągów ma tę wadę, że nie wykrywa końca łańcucha, więc musisz ręcznie sprawdzić, czy prefiks jest krótszy niż cały ciąg. (Jak poprawnie zrobiono w przykładowym progu, ale pominięto w powyższej
linijce

Więc nie ma przewagi nad rfind?
Андрей Вахрушев

26

Biorąc pod uwagę, że oba ciągi - argv[1]i "--foo"- są ciągami C, odpowiedź @ FelixDombek jest praktycznym najlepszym rozwiązaniem.

Widząc inne odpowiedzi, pomyślałem jednak, że warto zauważyć, że jeśli twój tekst jest już dostępny jako std::string, to istnieje proste, maksymalnie efektywne rozwiązanie, które nie zostało wspomniane do tej pory:

const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(strlen(foo));

A jeśli foo jest już ciągiem:

std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(foo.length());

6
rfind(x, 0) == 0należy naprawdę zdefiniować w standardzie jakostarts_with
por.

1
Nie, ponieważ rfind()(zamiast startswith()) jest bardzo nieefektywny - szuka do końca łańcucha.
ankostis

4
@ankostis rfind (x) szuka od końca do początku, dopóki nie znajdzie x. Ale rfind (x, 0) rozpoczyna wyszukiwanie od początku (pozycja = 0) do początku; więc wyszukuje tylko tam, gdzie wymaga wyszukiwania; nie wyszukuje od / do końca.
Anonimowy Tchórz,

18

Z C ++ 17 możesz korzystać std::basic_string_viewz C ++ 20 std::basic_string::starts_withlub std::basic_string_view::starts_with.

Zaletą std::string_vieww porównaniu do std::string- w odniesieniu do zarządzania pamięcią - jest to, że trzyma wskaźnik do „łańcucha” (ciągłej sekwencji obiektów podobnych do znaków) i zna jego rozmiar. Przykład bez przenoszenia / kopiowania ciągów źródłowych, aby uzyskać wartość całkowitą:

#include <exception>
#include <iostream>
#include <string>
#include <string_view>

int main()
{
    constexpr auto argument = "--foo=42"; // Emulating command argument.
    constexpr auto prefix = "--foo=";
    auto inputValue = 0;

    constexpr auto argumentView = std::string_view(argument);
    if (argumentView.starts_with(prefix))
    {
        constexpr auto prefixSize = std::string_view(prefix).size();
        try
        {
            // The underlying data of argumentView is nul-terminated, therefore we can use data().
            inputValue = std::stoi(argumentView.substr(prefixSize).data());
        }
        catch (std::exception & e)
        {
            std::cerr << e.what();
        }
    }
    std::cout << inputValue; // 42
}

1
@RolandIllig Nie, std::atoijest całkowicie w porządku. Zgłasza wyjątki od złych danych wejściowych (które są obsługiwane w tym kodzie). Czy miałeś na myśli coś innego?
Roi Danton

Mówisz o tym atoiz <cstdlib>? Dokumentacja mówi „nigdy nie rzuca wyjątki”.
Roland Illig

@RolandIllig Mam na myśli twój pierwszy komentarz. Wygląda na to, że omyłkowo mówisz atoizamiast std::atoi. Pierwszy jest niebezpieczny w użyciu, a drugi jest w porządku. Używam tego ostatniego w kodzie tutaj.
Roi Danton

Proszę mi udowodnić, że std::atoirzeczywiście rzuca wyjątek, powołując się na odpowiednie odniesienie. Dopóki tego nie zrobisz, nie wierzę ci, ponieważ byłoby to bardzo mylące, mając oba ::atoii std::atoidziałając w zupełnie inny sposób.
Roland Illig

4
@RolandIllig Dzięki za wytrwałość! Masz rację, std::atoizamiast tego zastosowano przeoczenie std::stoi. Naprawiłem to.
Roi Danton

12
text.substr(0, start.length()) == start

3
@GregorDoroschenko odpowiada na pytanie „sprawdź, czy ciąg zaczyna się od innej” części.
etarion

1
Wydajny i elegancki przy użyciu std :: string. Nauczyłem się z tego najwięcej.
Michael B

1
dodatkowe punkty za to, że są one-liner odpowiednimi do użytku zif (one-liner)
Adam.at.Epsilon 11.0419

@Roland Illig Dlaczego uważasz, że zachowanie w tej sprawie jest niezdefiniowane? Wyrażenie zwróci false, ponieważ substr zwraca łańcuch o tej samej długości co tekst zgodnie z en.cppreference.com/w/cpp/string/basic_string/substr
Macsinus

11

Przy użyciu STL może to wyglądać następująco:

std::string prefix = "--foo=";
std::string arg = argv[1];
if (prefix.size()<=arg.size() && std::equal(prefix.begin(), prefix.end(), arg.begin())) {
  std::istringstream iss(arg.substr(prefix.size()));
  iss >> foo_value;
}

2
Tak powinno być if (prefix.size()<=arg.size() && std::equal(...)).
Jared Grubb,

10

Ryzykując podpalenie za użycie konstruktów C, uważam, że ten sscanfprzykład jest bardziej elegancki niż większość rozwiązań wspomagających. I nie musisz się martwić o powiązanie, jeśli biegasz w dowolnym miejscu, które ma interpreter Pythona!

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    for (int i = 1; i != argc; ++i) {
        int number = 0;
        int size = 0;
        sscanf(argv[i], "--foo=%d%n", &number, &size);
        if (size == strlen(argv[i])) {
            printf("number: %d\n", number);
        }
        else {
            printf("not-a-number\n");
        }
    }
    return 0;
}

Oto kilka przykładowych danych wyjściowych, które pokazują, że rozwiązanie obsługuje początkowe / końcowe śmieci tak poprawnie jak równoważny kod Pythona i bardziej poprawnie niż cokolwiek innego atoi(co błędnie zignoruje nieliczbowy przyrostek).

$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number

7
Jeśli argv[i]tak "--foo=9999999999999999999999999", zachowanie jest niezdefiniowane (chociaż większość lub wszystkie implementacje powinny zachowywać się rozsądnie). Jestem przy założeniu 9999999999999999999999999 > INT_MAX.
Keith Thompson,

10

Używam std::string::compareowiniętej metody użyteczności, jak poniżej:

static bool startsWith(const string& s, const string& prefix) {
    return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}

5

Dlaczego nie skorzystać z GNU Getopts? Oto podstawowy przykład (bez kontroli bezpieczeństwa):

#include <getopt.h>
#include <stdio.h>

int main(int argc, char** argv)
{
  option long_options[] = {
    {"foo", required_argument, 0, 0},
    {0,0,0,0}
  };

  getopt_long(argc, argv, "f:", long_options, 0);

  printf("%s\n", optarg);
}

Dla następującego polecenia:

$ ./a.out --foo=33

Dostaniesz

33

5

Jeśli potrzebujesz kompatybilności z C ++ 11 i nie możesz użyć boosta, oto drop-in kompatybilny z boostem z przykładem użycia:

#include <iostream>
#include <string>

static bool starts_with(const std::string str, const std::string prefix)
{
    return ((prefix.size() <= str.size()) && std::equal(prefix.begin(), prefix.end(), str.begin()));
}

int main(int argc, char* argv[])
{
    bool usage = false;
    unsigned int foos = 0; // default number of foos if no parameter was supplied

    if (argc > 1)
    {
        const std::string fParamPrefix = "-f="; // shorthand for foo
        const std::string fooParamPrefix = "--foo=";

        for (unsigned int i = 1; i < argc; ++i)
        {
            const std::string arg = argv[i];

            try
            {
                if ((arg == "-h") || (arg == "--help"))
                {
                    usage = true;
                } else if (starts_with(arg, fParamPrefix)) {
                    foos = std::stoul(arg.substr(fParamPrefix.size()));
                } else if (starts_with(arg, fooParamPrefix)) {
                    foos = std::stoul(arg.substr(fooParamPrefix.size()));
                }
            } catch (std::exception& e) {
                std::cerr << "Invalid parameter: " << argv[i] << std::endl << std::endl;
                usage = true;
            }
        }
    }

    if (usage)
    {
        std::cerr << "Usage: " << argv[0] << " [OPTION]..." << std::endl;
        std::cerr << "Example program for parameter parsing." << std::endl << std::endl;
        std::cerr << "  -f, --foo=N   use N foos (optional)" << std::endl;
        return 1;
    }

    std::cerr << "number of foos given: " << foos << std::endl;
}

2

Możesz także użyć strstr:

if (strstr(str, substr) == substr) {
    // 'str' starts with 'substr'
}

ale myślę, że jest to dobre tylko dla krótkich łańcuchów, ponieważ musi on zapętlać cały łańcuch, gdy tak naprawdę łańcuch nie zaczyna się od „substr”.


2

Ok, dlaczego skomplikowane korzystanie z bibliotek i innych rzeczy? Obiekty łańcucha C ++ przeciążają operator [], więc możesz po prostu porównywać znaki. Podobnie jak to, co właśnie zrobiłem, ponieważ chcę wyświetlić listę wszystkich plików w katalogu i zignorować pliki niewidoczne oraz ... i. pseudofile.

while ((ep = readdir(dp)))
{
    string s(ep->d_name);
    if (!(s[0] == '.')) // Omit invisible files and .. or .
        files.push_back(s);
}

To takie proste..



2
@robertwb Google+ nie jest już dostępny
_Static_assert

0
std::string text = "--foo=98";
std::string start = "--foo=";

if (text.find(start) == 0)
{
    int n = stoi(text.substr(start.length()));
    std::cout << n << std::endl;
}

3
Byłoby wspaniale, jeśli unikniesz wklejania kodu bez wyjaśnienia kodu. Dziękuję Ci.
Reborn

1
Niewystarczający kod kontynuowałby wyszukiwanie poza początkiem ciągu.
ankostis

0

W C ++ 11 lub nowszym możesz używać find()ifind_first_of()

Przykład użycia find do znalezienia pojedynczego znaku:

#include <string>
std::string name = "Aaah";
size_t found_index = name.find('a');
if (found_index != std::string::npos) {
    // Found string containing 'a'
}

Przykład użycia znajdź, aby znaleźć pełny ciąg i zaczynając od pozycji 5:

std::string name = "Aaah";
size_t found_index = name.find('h', 3);
if (found_index != std::string::npos) {
    // Found string containing 'h'
}

Przykład użycia find_first_of()i tylko pierwszego znaku, aby wyszukać tylko na początku:

std::string name = ".hidden._di.r";
size_t found_index = name.find_first_of('.');
if (found_index == 0) {
    // Found '.' at first position in string
}

Powodzenia!


Dlaczego nie znaleźć? rfind (str, 0) nie będzie niepotrzebnie skanować całego łańcucha, aby dokonać wyboru, ponieważ nie może przejść dalej. Zobacz inne.
user2864740,

0

Ponieważ C ++ 11 std::regex_searchmoże również służyć do zapewniania jeszcze bardziej złożonego dopasowania wyrażeń. Poniższy przykład obsługuje również liczby zmiennoprzecinkowe std::stofi kolejne rzutowania naint .

Jednak parseIntmetoda pokazana poniżej może zgłosić std::invalid_argumentwyjątek, jeśli przedrostek nie zostanie dopasowany; można to łatwo dostosować w zależności od aplikacji:

#include <iostream>
#include <regex>

int parseInt(const std::string &str, const std::string &prefix) {
  std::smatch match;
  std::regex_search(str, match, std::regex("^" + prefix + "([+-]?(?=\\.?\\d)\\d*(?:\\.\\d*)?(?:[Ee][+-]?\\d+)?)$"));
  return std::stof(match[1]);
}

int main() {
    std::cout << parseInt("foo=13.3", "foo=") << std::endl;
    std::cout << parseInt("foo=-.9", "foo=") << std::endl;
    std::cout << parseInt("foo=+13.3", "foo=") << std::endl;
    std::cout << parseInt("foo=-0.133", "foo=") << std::endl;
    std::cout << parseInt("foo=+00123456", "foo=") << std::endl;
    std::cout << parseInt("foo=-06.12e+3", "foo=") << std::endl;

//    throw std::invalid_argument
//    std::cout << parseInt("foo=1", "bar=") << std::endl;

    return 0;
}

Rodzaj magii wzoru regularnego jest szczegółowo opisany w następującej odpowiedzi .

EDYCJA: poprzednia odpowiedź nie przeprowadziła konwersji na liczbę całkowitą.


0

Począwszy od C ++ 20, możesz użyć tej starts_withmetody.

std::string s = "abcd";
if (s.starts_with("abc")) {
    ...
}

-3
if(boost::starts_with(string_to_search, string_to_look_for))
    intval = boost::lexical_cast<int>(string_to_search.substr(string_to_look_for.length()));

Jest to całkowicie niesprawdzone. Zasada jest taka sama jak w języku Python. Wymaga Boost.StringAlgo i Boost.LexicalCast.

Sprawdź, czy łańcuch zaczyna się od drugiego łańcucha, a następnie pobierz podłańcuch („plasterek”) pierwszego łańcucha i przekonwertuj go za pomocą rzutowania leksykalnego.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.