diff --git a/applications/test/regex/Test-regex.C b/applications/test/regex/Test-regex.C index 6996d4445cee96fcea1dd060013df1a5750bbdb5..e79b7ba99828121e41bf305f0d44b03335b3adf1 100644 --- a/applications/test/regex/Test-regex.C +++ b/applications/test/regex/Test-regex.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation - \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. + \\/ M anipulation | Copyright (C) 2017-2018 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -30,52 +30,108 @@ Description #include "IOobject.H" #include "IFstream.H" #include "regExp.H" -#include "List.H" -#include "Tuple2.H" +#include "SubStrings.H" +#include "Switch.H" using namespace Foam; + +//- Simple structure for holding regex tests +struct regexTest +{ + bool expected; + string pattern; + string text; + + friend Istream& operator>>(Istream& is, struct regexTest& obj) + { + is.readBegin("struct"); + is >> obj.expected >> obj.pattern >> obj.text; + is.readEnd("struct"); + is.check(FUNCTION_NAME); + return is; + } + + friend Ostream& operator<<(Ostream& os, const struct regexTest& obj) + { + os << "( " + << obj.expected << " " << obj.pattern << " " << obj.text + << " )"; + return os; + } +}; + +// Needed for list output. Just treat everything as unequal. +bool operator!=(const struct regexTest&, const struct regexTest&) +{ + return true; +} + +// Simple output of match groups +static Ostream& operator<<(Ostream& os, const regExp::results_type& sm) +{ + for (std::smatch::size_type i = 1; i < sm.size(); ++i) + { + os << " " << sm.str(i); + } + + return os; +} + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // Main program: int main(int argc, char *argv[]) { - List<Tuple2<string, string>> rawList(IFstream("testRegexps")()); + List<regexTest> rawList(IFstream("testRegexps")()); Info<< "Test expressions:" << rawList << endl; IOobject::writeDivider(Info) << endl; - List<std::string> groups; + regExp::results_type match; + + // Expect some failures: + const bool throwingError = FatalError.throwExceptions(); // Report matches: - forAll(rawList, elemI) + for (const auto& testseq : rawList) { - const string& pat = rawList[elemI].first(); - const string& str = rawList[elemI].second(); - regExp re(pat); + const bool expected = testseq.expected; + const string& pat = testseq.pattern; + const string& str = testseq.text; + + Info<< "Test " << Switch(expected) << ": " + << str << " =~ m/" << pat.c_str() << "/ == "; - Info<< str << " =~ m/" << pat.c_str() << "/ == "; + regExp re; - if (re.match(str, groups)) + try { - Info<< "true"; - if (re.ngroups()) + re = pat; + + if (re.match(str, match)) { - Info<< nl << "groups: " << groups; + Info<< "true"; + if (re.ngroups()) + { + Info<< " (" << re.ngroups() << " groups):" << match; + } } - } - else - { - if (re.search(str)) + else if (re.search(str)) { - Info<< " partial match"; + Info<< "partial match"; } else { Info<< "false"; } + Info<< endl; + } + catch (Foam::error& err) + { + Info<< "Caught FatalError " << err << nl << endl; + continue; } - - Info<< endl; if (false) { @@ -96,68 +152,111 @@ int main(int argc, char *argv[]) Info<< nl << "test regExp(const char*) ..." << endl; string me("Mark"); - // Handling of null strings - if (regExp(nullptr).match(me)) + try { - Info<< "fail - matched: " << me << endl; + // Handling of null strings + if (regExp(nullptr).match(me)) + { + Info<< "fail - matched: " << me << endl; + } + else + { + Info<< "pass - null pointer is no expression" << endl; + } } - else + catch (Foam::error& err) { - Info<< "pass - null pointer is no expression" << endl; + Info<< "Caught FatalError " << err << nl << endl; } - // Normal match - if (regExp("[Mm]ar[ck]").match(me)) + try { - Info<< "pass - matched: " << me << endl; + // Normal match + if (regExp("[Mm]ar[ck]").match(me)) + { + Info<< "pass - matched: " << me << endl; + } + else + { + Info<< "no match" << endl; + } } - else + catch (Foam::error& err) { - Info<< "no match" << endl; + Info<< "Caught FatalError " << err << nl << endl; } - // Match ignore case - if (regExp("mar[ck]", true).match(me)) + try { - Info<< "pass - matched: " << me << endl; + // Match ignore case + if (regExp("mar[ck]", true).match(me)) + { + Info<< "pass - matched: " << me << endl; + } + else + { + Info<< "no match" << endl; + } } - else + catch (Foam::error& err) { - Info<< "no match" << endl; + Info<< "Caught FatalError " << err << nl << endl; } - // Embedded prefix for match ignore case - if (regExp("(?i)mar[ck]").match(me)) + try { - Info<< "pass - matched: " << me << endl; + // Embedded prefix for match ignore case + if (regExp("(?i)mar[ck]").match(me)) + { + Info<< "pass - matched: " << me << endl; + } + else + { + Info<< "no match" << endl; + } } - else + catch (Foam::error& err) { - Info<< "no match" << endl; + Info<< "Caught FatalError " << err << nl << endl; } - // Handling of empty expression - if (regExp("").match(me)) + try { - Info<< "fail - matched: " << me << endl; + // Handling of empty expression + if (regExp("").match(me)) + { + Info<< "fail - matched: " << me << endl; + } + else + { + Info<< "pass - no match on empty expression" << endl; + } } - else + catch (Foam::error& err) { - Info<< "pass - no match on empty expression" << endl; + Info<< "Caught FatalError " << err << nl << endl; } - // Embedded prefix - but expression is empty - if (regExp("(?i)").match(me)) + try { - Info<< "fail - matched: " << me << endl; + // Embedded prefix - but expression is empty + if (regExp("(?i)").match(me)) + { + Info<< "fail - matched: " << me << endl; + } + else + { + Info<< "pass - no match on empty expression" << endl; + } } - else + catch (Foam::error& err) { - Info<< "pass - no match on empty expression" << endl; + Info<< "Caught FatalError " << err << nl << endl; } + FatalError.throwExceptions(throwingError); - Info<< endl; + Info<< "\nDone" << nl << endl; return 0; } diff --git a/applications/test/regex/testRegexps b/applications/test/regex/testRegexps index 6f2d0e2935762290002cb6ee56383ca3c8271624..523780120139ae3a79c98243576a735e77393b54 100644 --- a/applications/test/regex/testRegexps +++ b/applications/test/regex/testRegexps @@ -10,27 +10,29 @@ // Pattern, String ( - ( "a.*" "abc" ) // true - ( "a.*" "bac" ) // false - partial match only - ( "a.*" "abcd" ) // true - ( "a.*" "def" ) // false - ( ".*a.*" "Abc" ) // false - ( "(?i).*a.*" "Abc" ) // true - ( "d(.*)f" "def" ) // true - ( + ( true "a.*" "abc" ) + ( true "a.*" "abc" ) + ( false "a.*" "bac" ) // partial match only + ( true "a.*" "abcd" ) + ( false "a.*" "def" ) + ( false ".*a.*" "Abc" ) + ( true "(?i).*a.*" "Abc" ) + ( true "d(.*)([xy]*)([f-p])" "def" ) + ( false "d(.*)([xy]*)([f-p])" "xxdef" ) + ( true " *([A-Za-z]+) *= *([^ /]+) *(//.*)?" " keyword = value // comment" - ) // true - + ) ( + false // partial match only "[[:digit:]]" "contains 1 or more digits" - ) // false - partial match only - + ) ( + true "[[:digit:]]+-[[:digit:]]+-[[:digit:]]+-[[:digit:]]+" "1-905-123-2234" - ) // true + ) ) diff --git a/src/OSspecific/POSIX/regExp.C b/src/OSspecific/POSIX/regExp.C index 0f9cd7cb21cc95cd68c10dfe2af111d61215a3ee..f53175571dae5d3bbbfe7753b2dc05c27b9929c3 100644 --- a/src/OSspecific/POSIX/regExp.C +++ b/src/OSspecific/POSIX/regExp.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2018 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -24,61 +24,35 @@ License \*---------------------------------------------------------------------------*/ #include "regExp.H" -#include "List.H" +#include "SubStrings.H" +#include "error.H" -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // -bool Foam::regExp::matchGrouping -( - const std::string& text, - List<std::string>& groups -) const +// Verify that the entire len was matched +static inline bool fullMatch(const regmatch_t& m, const regoff_t len) { - if (preg_ && !text.empty()) - { - size_t nmatch = ngroups() + 1; - regmatch_t pmatch[nmatch]; + return (m.rm_so == 0 && m.rm_eo == len); +} - // Also verify that the entire string was matched. - // pmatch[0] is the entire match - // pmatch[1..] are the (...) sub-groups - if - ( - regexec(preg_, text.c_str(), nmatch, pmatch, 0) == 0 - && (pmatch[0].rm_so == 0 && pmatch[0].rm_eo == label(text.size())) - ) - { - groups.setSize(ngroups()); - label groupI = 0; - for (size_t matchI = 1; matchI < nmatch; matchI++) - { - if (pmatch[matchI].rm_so != -1 && pmatch[matchI].rm_eo != -1) - { - groups[groupI] = text.substr - ( - pmatch[matchI].rm_so, - pmatch[matchI].rm_eo - pmatch[matchI].rm_so - ); - } - else - { - groups[groupI].clear(); - } - groupI++; - } +// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // - return true; - } +bool Foam::regExp::clear() +{ + if (preg_) + { + regfree(preg_); + delete preg_; + preg_ = nullptr; + + return true; } - groups.clear(); return false; } -// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // - bool Foam::regExp::set(const char* pattern, bool ignoreCase) { clear(); @@ -136,26 +110,11 @@ bool Foam::regExp::set(const std::string& pattern, bool ignoreCase) } -bool Foam::regExp::clear() -{ - if (preg_) - { - regfree(preg_); - delete preg_; - preg_ = nullptr; - - return true; - } - - return false; -} - - std::string::size_type Foam::regExp::find(const std::string& text) const { if (preg_ && !text.empty()) { - size_t nmatch = 1; + const size_t nmatch = 1; regmatch_t pmatch[1]; if (regexec(preg_, text.c_str(), nmatch, pmatch, 0) == 0) @@ -170,21 +129,20 @@ std::string::size_type Foam::regExp::find(const std::string& text) const bool Foam::regExp::match(const std::string& text) const { - if (preg_ && !text.empty()) + const auto len = text.size(); + + if (preg_ && len) { - size_t nmatch = 1; + const size_t nmatch = 1; regmatch_t pmatch[1]; - // Also verify that the entire string was matched - // pmatch[0] is the entire match - if + // Verify that the entire string was matched + // - [0] is the entire match result + return ( regexec(preg_, text.c_str(), nmatch, pmatch, 0) == 0 - && (pmatch[0].rm_so == 0 && pmatch[0].rm_eo == regoff_t(text.size())) - ) - { - return true; - } + && fullMatch(pmatch[0], len) + ); } return false; @@ -194,10 +152,54 @@ bool Foam::regExp::match(const std::string& text) const bool Foam::regExp::match ( const std::string& text, - List<std::string>& groups + SubStrings<std::string>& matches ) const { - return matchGrouping(text, groups); + matches.clear(); + + const auto len = text.size(); + if (preg_ && len) + { + const size_t nmatch = ngroups() + 1; + regmatch_t pmatch[nmatch]; + + // Verify that the entire string was matched + // - [0] is the entire match result + // - [1..] are the match groups (1..) + if + ( + regexec(preg_, text.c_str(), nmatch, pmatch, 0) != 0 + || !fullMatch(pmatch[0], len) + ) + { + return false; + } + + matches.reserve(nmatch); + + for (size_t matchi = 0; matchi < nmatch; ++matchi) + { + const auto& mat = pmatch[matchi]; + + if (mat.rm_so != -1 && mat.rm_eo != -1) + { + matches.append + ( + text.cbegin() + mat.rm_so, + text.cbegin() + mat.rm_eo + ); + } + else + { + // This may be misleading... + matches.append(text.cbegin(), text.cbegin()); + } + } + + return true; + } + + return false; } diff --git a/src/OSspecific/POSIX/regExp.H b/src/OSspecific/POSIX/regExp.H index c5c78929092cda94c6c3038b8677814c332ca2ad..59417ea7a4a576c3e23646f269101ef98784db2d 100644 --- a/src/OSspecific/POSIX/regExp.H +++ b/src/OSspecific/POSIX/regExp.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation - \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. + \\/ M anipulation | Copyright (C) 2017-2018 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -35,6 +35,7 @@ See also These differ somewhat from \c Perl and \c sed regular expressions. SourceFiles + regExpI.H regExp.C \*---------------------------------------------------------------------------*/ @@ -50,8 +51,8 @@ SourceFiles namespace Foam { -// Forward declaration of classes -template<class T> class List; +// Forward declarations +template<class String> class SubStrings; /*---------------------------------------------------------------------------*\ @@ -65,34 +66,22 @@ class regExp //- Precompiled regular expression regex_t* preg_; +public: - // Private Member Functions - - //- Disallow default bitwise copy construct - regExp(const regExp&) = delete; - - //- Disallow default bitwise assignment - void operator=(const regExp&) = delete; - - //- Return true if it matches and sets the sub-groups matched. - bool matchGrouping - ( - const std::string& text, - List<std::string>& groups - ) const; - + //- Type for matches + typedef SubStrings<std::string> results_type; -public: // Static Member Functions - //- Is character a regular expression meta-character? - // any character: '.' \n - // quantifiers: '*', '+', '?' \n - // grouping: '(', '|', ')' \n - // range: '[', ']' \n + //- Test if character appears to be a regular expression meta-character + // \return true if character is one of the following: + // - any character: '.' \n + // - quantifiers: '*', '+', '?' \n + // - grouping: '(', '|', ')' \n + // - range: '[', ']' \n // - // Don't bother checking for '{digit}' bounds + // \note The presence of '{', '}' regex bounds is not considered inline static bool meta(const char c); @@ -101,6 +90,12 @@ public: //- Construct null inline regExp(); + //- Copy construct - disallowed + regExp(const regExp&) = delete; + + //- Move construct + inline regExp(regExp&& rgx); + //- Construct from character array inline explicit regExp(const char* pattern); @@ -113,9 +108,6 @@ public: //- Construct from string, optionally ignore case inline regExp(const std::string& pattern, bool ignoreCase); - //- Move construct - inline regExp(regExp&& rgx); - //- Destructor inline ~regExp(); @@ -123,27 +115,18 @@ public: // Member functions - // Access + // Access //- Return true if a precompiled expression does not exist inline bool empty() const; - //- Does a precompiled expression exist? + //- Return true if a precompiled expression exists inline bool exists() const; //- The number of capture groups for a non-empty expression inline unsigned ngroups() const; - - // Editing - - //- Compile pattern into a regular expression, optionally ignore case. - // \return True if the pattern was compiled - bool set(const char* pattern, bool ignoreCase=false); - - //- Compile pattern into a regular expression, optionally ignore case - // \return True if the pattern was compiled - bool set(const std::string& pattern, bool ignoreCase=false); + // Editing //- Clear expression. // \return True if expression had existed prior to the clear. @@ -152,8 +135,15 @@ public: //- Swap contents inline void swap(regExp& rgx); + //- Compile pattern into a regular expression, optionally ignore case. + // \return True if the pattern was compiled + bool set(const char* pattern, bool ignoreCase=false); - // Matching/Searching + //- Compile pattern into a regular expression, optionally ignore case. + // \return True if the pattern was compiled + bool set(const std::string& pattern, bool ignoreCase=false); + + // Matching/Searching //- Find position within the text. // \return The index where it begins or string::npos if not found @@ -163,9 +153,10 @@ public: // The begin-of-line (^) and end-of-line ($) anchors are implicit bool match(const std::string& text) const; - //- True if the regex matches the text, set the sub-groups matched. + //- True if the regex matches the text, set the matches. + // The first group starts at index 1 (0 is the entire match). // The begin-of-line (^) and end-of-line ($) anchors are implicit - bool match(const std::string& text, List<std::string>& groups) const; + bool match(const std::string& text, results_type& matches) const; //- Return true if the regex was found within the text inline bool search(const std::string& text) const; @@ -176,17 +167,20 @@ public: //- Perform match on text inline bool operator()(const std::string& text) const; - //- Assign and compile pattern from a character array - // Always case sensitive - inline void operator=(const char* pattern); - - //- Assign and compile pattern from string - // Always case sensitive - inline void operator=(const std::string& pattern); + //- Copy assignment - disallowed + void operator=(const regExp&) = delete; //- Move assignment inline void operator=(regExp&& rgx); + //- Assign and compile pattern from a character array. + // Matching is case sensitive. + inline void operator=(const char* pattern); + + //- Assign and compile pattern from string. + // Matching is case sensitive. + inline void operator=(const std::string& pattern); + }; diff --git a/src/OSspecific/POSIX/regExpI.H b/src/OSspecific/POSIX/regExpI.H index 60c7da3053b2d30a4af82700ff277a8bc2b3a488..6586de160c408309ad0e9dcdbec02534f6cd88e1 100644 --- a/src/OSspecific/POSIX/regExpI.H +++ b/src/OSspecific/POSIX/regExpI.H @@ -136,22 +136,22 @@ inline bool Foam::regExp::operator()(const std::string& text) const } -inline void Foam::regExp::operator=(const char* pattern) +inline void Foam::regExp::operator=(regExp&& rgx) { - set(pattern); + clear(); + swap(rgx); } -inline void Foam::regExp::operator=(const std::string& pattern) +inline void Foam::regExp::operator=(const char* pattern) { set(pattern); } -inline void Foam::regExp::operator=(regExp&& rgx) +inline void Foam::regExp::operator=(const std::string& pattern) { - clear(); - swap(rgx); + set(pattern); } diff --git a/src/OpenFOAM/primitives/strings/lists/SubStrings.H b/src/OpenFOAM/primitives/strings/lists/SubStrings.H index 3e37fd7edd517ed6a5a743193fe559263be97ec1..0f2a690d6d1e154c4213af8f84af1cef00e5de6f 100644 --- a/src/OpenFOAM/primitives/strings/lists/SubStrings.H +++ b/src/OpenFOAM/primitives/strings/lists/SubStrings.H @@ -66,8 +66,7 @@ public: // Constructors //- Construct null - SubStrings() - {} + SubStrings() = default; // Member Functions @@ -114,6 +113,13 @@ public: return this->back(); } + + //- Get element pos, converted to a string type. + String str(size_t pos) const + { + return (*this)[pos].str(); + } + }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormatCore.C b/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormatCore.C index b55e86ea1f4e65d42dab9da6fde1115f60ac124a..2462f7e569ab1eff9da0819de4624703ebe37f69 100644 --- a/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormatCore.C +++ b/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormatCore.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation - \\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd. + \\/ M anipulation | Copyright (C) 2016-2018 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -27,6 +27,7 @@ License #include "clock.H" #include "regExp.H" #include "IFstream.H" +#include "SubStrings.H" // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // @@ -35,10 +36,7 @@ License // don't bother with the older comma-delimited format Foam::Map<Foam::word> -Foam::fileFormats::STARCDsurfaceFormatCore::readInpCellTable -( - ISstream& is -) +Foam::fileFormats::STARCDsurfaceFormatCore::readInpCellTable(ISstream& is) { Map<word> lookup; @@ -47,7 +45,7 @@ Foam::fileFormats::STARCDsurfaceFormatCore::readInpCellTable return lookup; } - const regExp ctnameRE + const regExp ctname ( " *CTNA[^ ]*" // keyword - min 4 chars "[[:space:]]+" // space delimited @@ -58,13 +56,14 @@ Foam::fileFormats::STARCDsurfaceFormatCore::readInpCellTable ); string line; - List<std::string> groups; + regExp::results_type groups; + while (is.good() && is.getLine(line).good()) { - if (ctnameRE.match(line, groups)) + if (ctname.match(line, groups)) { - const label tableId = atoi(groups[0].c_str()); - const word tableName = word::validate(groups[1], true); + const label tableId = readLabel(groups.str(1)); + const word tableName = word::validate(groups.str(2), true); if (!tableName.empty()) {