2017-07-24 5 views
-1

Unten, ich produziere sowohl gebrochenen Code und eine feste Version der gleichen. Das Problem ist, dass ich mir nicht vollständig erklären kann, warum das erstere nicht funktioniert, aber das letztere. Ich muss natürlich ein sehr grundlegendes Konzept der C++ - Sprache überprüfen: Könnten Sie Hinweise geben, was ich überprüfen sollte, und möglicherweise auch eine Erklärung geben, warum ich die Ergebnisse bekomme, die ich mit dem kaputten Code erhalte?Grundlegende Konzepte mit Std :: String Referenzen, Std :: Regex und Boost :: Dateisystem

In dem Verzeichnis "../docs/", auf das im Code verwiesen wird, habe ich einfach den "touch" -Befehl unter Linux verwendet, um eine Anzahl doc ...... html-Dateien verschiedener Länge zu erstellen.

#include <iostream> 
#include <regex> 
#include <boost/filesystem.hpp> 

namespace fs = boost::filesystem; 

int main() { 
     fs::path p("../docs/"); 

     for (auto& dir_it : fs::directory_iterator(p)) { 
       std::regex re = std::regex("^(doc[a-z]+)\\.html$"); 
       std::smatch matches; 
       // BROKEN HERE: 
       if (std::regex_match(dir_it.path().filename().string(), matches, re)) { 
         std::cout << "\t\t" <<dir_it.path().filename().string(); 
         std::cout << "\t\t\t" << matches[1] << std::endl; 
       } 
     } 

     return 0; 
} 

Produziert:

 documentati.html      ati 
     documentationt.html      �}:ationt 
     document.html     document 
     documenta.html     documenta 
     docume.html      docume 
     documentat.html     documentat 
     docum.html      docum 
     documentatio.html      ��:atio 
     documen.html     documen 
     docu.html      docu 
     documentation.html      ��:ation 
     documaeuaoeu.html      ��:aoeu 

Anmerkung 1: Die obige Fehler bei Dateinamen ausgelöst wird, die über eine bestimmte Länge sind. Ich verstehe nur, weil das std :: string-Objekt die Größe selbst ändert.

Anmerkung 2: Der obige Code ist sehr ähnlich als der Code in der folgenden Frage verwendet, aber mit boost :: regex_match statt std :: regex_match: Can I use a mask to iterate files in a directory with Boost?
Es wird verwendet für mich vor als gut zu funktionieren, aber jetzt benutze ich GCC 5.4 anstelle von GCC 4.6, std :: regex anstelle von boost :: regex, C++ 11 und eine viel neuere Version von boost :: filesystem. Welche Änderung ist relevant, was dazu geführt hat, dass Arbeitscode kaputt gegangen ist?

Korrigiert:

#include <iostream> 
#include <regex> 
#include <boost/filesystem.hpp> 

namespace fs = boost::filesystem; 

int main() { 
     fs::path p("../docs/"); 

     for (auto& dir_it : fs::directory_iterator(p)) { 
       std::regex re = std::regex("^(doc[a-z]+)\\.html$"); 
       std::smatch matches; 
       std::string p = dir_it.path().filename().string(); 
       if (std::regex_match(p, matches, re)) { 
         std::cout << "\t\t" <<dir_it.path().filename().string(); 
         std::cout << "\t\t\t" << matches[1] << std::endl; 
       } 
     } 

     return 0; 
} 

produziert:

 documentati.html      documentati 
     documentationt.html      documentationt 
     document.html     document 
     documenta.html     documenta 
     docume.html      docume 
     documentat.html     documentat 
     docum.html      docum 
     documentatio.html      documentatio 
     documen.html     documen 
     docu.html      docu 
     documentation.html      documentation 
     documaeuaoeu.html      documaeuaoeu 

mit boost 1.62.0-r1 und gcc (Gentoo 5.4.0-r3), die boost :: filesystem Dokumentation erscheint nicht auf bietet einen klaren Hinweis darauf, welchen Weg() Dateiname() string() liefert:.. http://www.boost.org/doc/libs/1_62_0/libs/filesystem/doc/reference.html#path-filename
es scheint, dass es abhängt:
Why does boost::filesystem::path::string() return by value on Windows and by reference on POSIX?

Antwort

1

std::smatch speichert keine Kopie der Zeichen, aus denen die Übereinstimmung besteht, sondern nur Paare von Iteratoren in der ursprünglichen Zeichenfolge, nach der gesucht wurde. Das erste Fragment übergibt eine temporäre Zeichenfolge an std::regex_match. Zu der Zeit, die Sie zur Überprüfung matches kommen, ist diese Zeichenfolge weg und die Iteratoren von matches gehalten werden alle baumeln. Das Programm weist dann undefiniertes Verhalten auf, indem es versucht, sie zu verwenden.

Im zweiten Fragment ist die an std::regex_match übergebene Zeichenfolge noch am Leben, wenn matches verwendet wird, also ist alles in Ordnung.


Anmerkung 1: Das ist das Problem anders auf Dateinamen Länge Manifeste abhängig ist wahrscheinlich aufgrund der geringen String-Optimierung. Es ist üblich, dass std::string Implementierungen einen kleinen Puffer in der Klasseninstanz reservieren und kurze Zeichenfolgen dort speichern; während auf dem Heap längere Zeichenfolgen zugewiesen werden. Es ist möglich, dass der Stapelspeicherbereich, der zuvor von der temporären Zeichenfolge belegt wurde, noch intakt ist, während der Heapspeicherbereich für andere Zuordnungen wiederverwendet wurde. Das Programm zeigt undefiniertes Verhalten in beiden Richtungen - "scheint zu funktionieren" ist eine mögliche Manifestation von UB.


Anmerkung 2: Es spielt keine große Rolle, ob path::string() kehrt nach Wert oder als Referenz. path::filename() gibt nach Wert zurück, also dir_it.path().filename().string() würde zu früh entweder zerstört werden, ob es ein temporäres dir_it.path().filename() Objekt ist, oder von string() on the fly hergestellt.

+0

Oh! Jetzt, wo ich es sehe, scheint es offensichtlich. +1. Vielen Dank für Ihre Zeit. – augustin

Verwandte Themen