2016-08-12 3 views
3

Ich habe eine hässliche Durcheinander einer Zeichenfolge, die aus mehreren URIs besteht.Mit std :: Regex zu filtern Eingang

:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/0_301_0.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02011.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02012.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02110000.svg 

Was würde ich tun möchte, ist :/., jedes Vorkommen der Zeichen Streifen aus, so kann ich eine einzelne Zeichenfolge, die ein gültiger Dateiname sein würde.

ich diese einfach in regulären Ausdruck geschrieben habe, um jus zu tun, die: [^(:/,.)] Es scheint der richtige reguläre Ausdruck zu sein, nach http://www.regexpal.com/.

Wenn ich jedoch den folgenden C++ - Code ausführe, bekomme ich nicht das zurück, was ich erwartet habe (nur alphanumerische Zeichen und Unterstriche), ich bekomme nur das erste alphanumerische Zeichen in der folgenden Reihenfolge zurück: S.

Was mache ich falsch mit Std :: Regex, oder ist mein Regex-Ausdruck ausgeschaltet?

#include <iostream> 
#include <regex> 
#include <string> 

static const std::string filenames {R"(:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/0_301_0.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02011.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02012.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02110000.svg)"}; 
static const std::regex filename_extractor("[^(:/,.)]"); 

int main() { 
    std::smatch filename_match; 
    if(std::regex_search(filenames, filename_match, filename_extractor)) 
    { 
     std::cout << "Number of filenames: " << filename_match.size() << std::endl; 
     for(std::size_t i = 0; i < filename_match.size(); ++i) 
     { 
      std::cout << i << ": " << filename_match[i] << std::endl; 
     } 
    } 

    return 0; 
} 
+4

Ich vermute, dass Sie 'std :: Regex_replace 'wollen, aber das ist wahrscheinlich besser, Regex überhaupt nicht zu verwenden. Schauen Sie sich vielleicht [std :: remove_if] (http://en.cppreference.com/w/cpp/algorithm/remove) an. – Galik

+0

Ich verstehe es nicht. Ihre Zeichenfolge enthält ',: /', nicht ': /,'? Kannst du das nicht stattdessen teilen? – rustyx

Antwort

3

Die size() von std::smatch gibt die Nummer des Unterausdrucks + 1 zurück (mit ( und ), was du nicht hast).

Lösung

Sie müssen std::regex_search wiederholt anrufen, oder std::regex_iterator verwenden.

Darüber hinaus hat Ihre Regex tatsächlich nur nach einem einzelnen Zeichen gesucht. Sie müssen + verwenden, um nach den längsten Zeichenfolgen zu suchen: [^(:/,.)]+.

Hier ist der Code, das Beispiel von cppreference.com eingebautem

#include <iostream> 
#include <iterator> 
#include <regex> 
#include <string> 

static const std::string filenames {R"(:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/0_301_0.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02011.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02012.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02110000.svg)"}; 
static const std::regex filename_extractor("[^(:/,.)]+"); 

int main() { 
    auto files_begin = std::sregex_iterator(filenames.begin(), filenames.end(), filename_extractor); 

    for (auto i = files_begin; i != std::sregex_iterator(); ++i) { 
     std::string filename = i->str(); 
     std::cout << filename << '\n'; 
    } 

    return 0; 
} 

Doch diese gibt auch die Zwischen "Verzeichnisse". Wenn Sie die regex [^(:,)]+ verwenden, erhalten Sie das Ergebnis ich Sie erwarten würden, haben wollte:

/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/0_301_0.svg 
/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02011.svg 
/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02012.svg 
/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02110000.svg 

Ihr Problem erklärt

std::regex_search sucht nur nach dem ersten Vorkommen des regulären Ausdrucks, und alle Unterausdrücke innerhalb.

Zum Beispiel entspricht der Ausdruck ab([cd])([ef]) der Zeichenfolge xxabcfxxabdef. Die erste Übereinstimmung ist der Teil abcf, wobei c die Übereinstimmung für den ersten Unterausdruck [cd] und e die Übereinstimmung für den zweiten Unterausdruck [ef] ist.Die zweite Übereinstimmung ist der Teil abde (nicht abdef!), Wobei e die Übereinstimmung für den zweiten Unterausdruck ist.

Mit std::regex_search suchen Sie nach der ersten Übereinstimmung, und der Matcher gibt Ihnen die vollständige erste Übereinstimmung und die Übereinstimmungen für die Unterausdrücke zurück. Wenn Sie weitere Übereinstimmungen suchen möchten, müssen Sie die Suche aus dem Rest der Zeichenfolge (std::smatch::suffix()) starten.

Darüber hinaus stimmt der Regex [ef] nur mit einem einzelnen Zeichen überein. [ef]+ würde die längste Sequenz von e s und f s entsprechen. Somit würde die Übereinstimmung für den zweiten Unterausdruck von ab([cd])([ef]) für die obige Zielzeichenfolge ef und nicht nur e entsprechen.

2

ich std::regex_replace denken ist, was Sie brauchen hier:

#include <regex> 
#include <string> 
#include <iostream> 

const std::string filenames {R"(:/MIL_STD/0_3.svg,:/SS/2525D/02011.svg)"}; 
const std::regex filename_extractor("[(:/,.)]"); 

int main() 
{ 
    std::string r; 

    std::regex_replace(std::back_inserter(r), 
     filenames.begin(), filenames.end(), filename_extractor, ""); 

    std::cout << "before: " << filenames << '\n'; 
    std::cout << " after: " << r << '\n'; 
} 

aber ich denke, regex wahrscheinlich übertrieben ist Zeichen für das Entfernen Sie diese effizienter machen kann mit std::remove_copy_if:

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

const std::string filenames {R"(:/MIL_STD/0_3.svg,:/SS/2525D/02011.svg)"}; 
const std::string filename_extractor("(:/,.)"); 

int main() 
{ 
    std::string r; 

    std::remove_copy_if(filenames.begin(), filenames.end(), 
     std::back_inserter(r), [](char c) 
    { 
     return filename_extractor.find(c) != std::string::npos; 
    }); 

    std::cout << "before: " << filenames << '\n'; 
    std::cout << " after: " << r << '\n'; 
}