Czy gcc 4.8 lub starszy zawiera błędy w wyrażeniach regularnych?

101

Próbuję użyć std :: regex w fragmencie kodu C ++ 11, ale wygląda na to, że obsługa jest trochę błędna. Przykład:

#include <regex>
#include <iostream>

int main (int argc, const char * argv[]) {
    std::regex r("st|mt|tr");
    std::cerr << "st|mt|tr" << " matches st? " << std::regex_match("st", r) << std::endl;
    std::cerr << "st|mt|tr" << " matches mt? " << std::regex_match("mt", r) << std::endl;
    std::cerr << "st|mt|tr" << " matches tr? " << std::regex_match("tr", r) << std::endl;
}

wyjścia:

st|mt|tr matches st? 1
st|mt|tr matches mt? 1
st|mt|tr matches tr? 0

po skompilowaniu za pomocą gcc (MacPorts gcc47 4.7.1_2) 4.7.1, albo z

g++ *.cc -o test -std=c++11
g++ *.cc -o test -std=c++0x

lub

g++ *.cc -o test -std=gnu++0x

Poza tym wyrażenie regularne działa dobrze, jeśli mam tylko dwa alternatywne wzorce, np. st|mtWięc wygląda na to, że ostatni z jakichś powodów nie jest dopasowany. Kod działa dobrze z kompilatorem Apple LLVM.

Jakieś pomysły, jak rozwiązać ten problem?

Zaktualizuj jednym możliwym rozwiązaniem jest użycie grup do wdrożenia wielu alternatyw, np (st|mt)|tr.

tunnuz
źródło
9
Tak, obsługa libstdc ++ <regex>jest niekompletna. W czym możemy Ci pomóc?
kennytm
10
Aby regexzapoznać się ze stanem w libstdc ++, patrz gcc.gnu.org/onlinedocs/libstdc++/manual/…
ecatmur
52
A tak na poważnie, kto pomyślał, że wysłanie implementacji regex_search, która zwraca tylko „zwraca fałsz”, było dobrym pomysłem? „Och, udokumentowaliśmy to” wydaje się słabą odpowiedzią.
Paul Rubel,
4
@ AK4749: to nie jest błąd. Po prostu nie został zaimplementowany. Chociaż ilość przypadków, w których pojawia się to pytanie, jest niepokojąca, zwłaszcza, że ​​nic się nie zmieniło w libstdc ++ <regex>w ciągu ostatnich 3-4 lat (np. Pozostaje niezaimplementowane).
rubenvb
5
@KeithThompson, chociaż prawdą <regex>jest, że jest dostarczane przez libstdc ++ (standardową bibliotekę GCC), a nie gcc(interfejs kompilatora), jest częścią GCC (projektu). Zobacz „libstdc ++ - v3 jest rozwijane i wydawane jako część GCC” . Jeśli twoja dystrybucja zdecyduje się podzielić ją na osobny pakiet, nie ma to nic wspólnego z GCC.
Jonathan Wakely

Odpowiedzi:

170

<regex> został zaimplementowany i wydany w GCC 4.9.0.

W Twojej (starszej) wersji GCC nie jest zaimplementowane .

Ten prototypowy <regex>kod został dodany, gdy cała obsługa GCC C ++ 0x była wysoce eksperymentalna, śledząc wczesne wersje robocze C ++ 0x i udostępniając ją ludziom do eksperymentowania. Pozwoliło to ludziom znaleźć problemy i przekazać opinię komisji normalizacyjnej przed sfinalizowaniem standardu. W tym czasie wiele osób byli wdzięczni, że miał dostęp do krwawienia krawędzi wyposażony długo zanim C ++ 11 i był gotowy przed wielu innych kompilatorów warunkiem żadnego wsparcia, a feedback naprawdę pomogło poprawić c ++ 11. To była Dobra Rzecz TM .

<regex>Kod nigdy nie był w stanie przydatnym, ale został dodany jako work-in-progress jak wiele innych bitów kodu w tym czasie. Został zarejestrowany i udostępniony innym do współpracy, jeśli chcieli, z zamiarem, że w końcu zostanie ukończony.

Tak często działa open source: wypuszczaj wcześnie, publikuj często - niestety w przypadku, <regex>gdy dostaliśmy tylko wczesną część dobrze, a nie często część, która zakończyłaby wdrażanie.

Większość części biblioteki była bardziej kompletna i jest obecnie prawie w pełni zaimplementowana, ale <regex>nie została, więc pozostała w tym samym niedokończonym stanie od momentu dodania.

A tak na poważnie, kto pomyślał, że wysłanie implementacji regex_search, która zwraca tylko „zwraca fałsz”, było dobrym pomysłem?

Nie był to taki zły pomysł kilka lat temu, kiedy C ++ 0x wciąż był w toku i dostarczyliśmy wiele częściowych implementacji. Nikt nie pomyślał, że pozostanie bezużyteczny przez tak długi czas, więc z perspektywy czasu może powinien był zostać wyłączony i wymagał makra lub opcji wbudowanej, aby go włączyć. Ale ten statek płynął dawno temu. Istnieją wyeksportowane symbole z libstdc ++., Więc biblioteka zależna od kodu wyrażenia regularnego, więc proste jej usunięcie (na przykład w GCC 4.8) nie byłoby trywialne.

Jonathan Wakely
źródło
13

Wykrywanie funkcji

To jest fragment do wykrycia, czy libstdc++implementacja jest zaimplementowana z preprocesorem C definiuje:

#include <regex>
#if __cplusplus >= 201103L &&                             \
    (!defined(__GLIBCXX__) || (__cplusplus >= 201402L) || \
        (defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \
         defined(_GLIBCXX_REGEX_STATE_LIMIT)           || \
             (defined(_GLIBCXX_RELEASE)                && \
             _GLIBCXX_RELEASE > 4)))
#define HAVE_WORKING_REGEX 1
#else
#define HAVE_WORKING_REGEX 0
#endif

Makra

  • _GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMITjest określone w bits/regex.tccw4.9.x
  • _GLIBCXX_REGEX_STATE_LIMITjest określone w bits/regex_automatron.hw5+
  • _GLIBCXX_RELEASEzostał dodany 7+w wyniku tej odpowiedzi i jest wersją główną GCC

Testowanie

Możesz to przetestować z GCC w ten sposób:

cat << EOF | g++ --std=c++11 -x c++ - && ./a.out
#include <regex>

#if __cplusplus >= 201103L &&                             \
    (!defined(__GLIBCXX__) || (__cplusplus >= 201402L) || \
        (defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \
         defined(_GLIBCXX_REGEX_STATE_LIMIT)           || \
             (defined(_GLIBCXX_RELEASE)                && \
             _GLIBCXX_RELEASE > 4)))
#define HAVE_WORKING_REGEX 1
#else
#define HAVE_WORKING_REGEX 0
#endif

#include <iostream>

int main() {
  const std::regex regex(".*");
  const std::string string = "This should match!";
  const auto result = std::regex_search(string, regex);
#if HAVE_WORKING_REGEX
  std::cerr << "<regex> works, look: " << std::boolalpha << result << std::endl;
#else
  std::cerr << "<regex> doesn't work, look: " << std::boolalpha << result << std::endl;
#endif
  return result ? EXIT_SUCCESS : EXIT_FAILURE;
}
EOF

Wyniki

Oto kilka wyników dla różnych kompilatorów:


$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ./a.out
<regex> doesn't work, look: false

$ gcc --version
gcc (GCC) 6.2.1 20160830
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ./a.out
<regex> works, look: true

$ gcc --version
gcc (Debian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ./a.out
<regex> works, look: true

$ gcc --version
gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ./a.out
<regex> works, look: true

$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ./a.out
<regex> works, look: true

$ gcc --version
gcc (GCC) 6.2.1 20160830
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ clang --version
clang version 3.9.0 (tags/RELEASE_390/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ ./a.out  # compiled with 'clang -lstdc++'
<regex> works, look: true

Oto smoki

Jest to całkowicie nieobsługiwane i polega na wykrywaniu prywatnych makr, które programiści GCC umieścili w bits/regex*nagłówkach. W każdej chwili mogli się zmienić i odejść . Miejmy nadzieję, że nie zostaną one usunięte w obecnych wydaniach 4.9.x, 5.x, 6.x, ale mogą zniknąć w wydaniach 7.x.

Jeśli programiści GCC dodali #define _GLIBCXX_HAVE_WORKING_REGEX 1(lub coś, podpowiedź, podpowiedź, podsunięcie) w wersji 7.x, która się utrzymała, ten fragment może zostać zaktualizowany, aby uwzględnić to, a późniejsze wersje GCC będą działać z powyższym fragmentem.

O ile wiem, wszystkie inne kompilatory działają <regex>, __cplusplus >= 201103Lale YMMV.

Oczywiście byłoby to całkowicie zepsute, gdyby ktoś zdefiniował makra _GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMITlub _GLIBCXX_REGEX_STATE_LIMITpoza stdc++-v3nagłówkami.

Matt Clarkson
źródło
Bardzo dobrze! Chciałem zasugerować sprawdzenie makra ochrony nagłówka z jednego z nagłówków, który jest nowy w GCC 4.9, ale nie mają one zabezpieczeń: - \ Makra nie zmieniają się dla GCC 7, ale teoretycznie mogłyby to zrobić dla GCC 8+, więc prosimy o zgłoszenie prośby o ulepszenie na gcc.gnu.org/bugzilla z prośbą o wpisanie czegoś takiego jak _GLIBCXX_REGEX_IS_OK_NOW_KTHXBAIw nagłówkach, aby nie zostało zapomniane - dzięki!
Jonathan Wakely
1
@JonathanWakely dodał 78905 . Nie jestem pewien, jak zrobić z tego błąd ulepszenia, ale jest teraz w systemie.
Matt Clarkson
1

W tej chwili (używając std = c ++ 14 w g ++ (GCC) 4.9.2) nadal nie akceptuje regex_match.

Oto podejście, które działa jak regex_match, ale zamiast tego używa sregex_token_iterator. Działa z g ++.

string line="1a2b3c";
std::regex re("(\\d)");
std::vector<std::string> inVector{
    std::sregex_token_iterator(line.begin(), line.end(), re, 1), {}
};

//prints all matches
for(int i=0; i<inVector.size(); ++i)
    std::cout << i << ":" << inVector[i] << endl;

wydrukuje 1 2 3

możesz przeczytać odniesienie do sregex_token_iterator w: http://en.cppreference.com/w/cpp/regex/regex_token_iterator

Luis Orantes
źródło
1
„W tej chwili (przy użyciu std = c ++ 14 w g ++ (GCC) 4.9.2) nadal nie akceptuje regex_match”. To nieprawda, prawdopodobnie źle go używasz.
Jonathan Wakely,
1
Twój kod nie jest „podejściem, które działa jak regex_match”, ponieważ ta funkcja próbuje dopasować podciągi, a nie cały ciąg, więc nadal uważam, że używasz jej źle. Możesz to jednak zrobić std::regex_search, zobacz wandbox.org/permlink/rLbGyYcYGNsBWsaB
Jonathan Wakely