ENH: additional fileName methods

- add an extension to the file name
   - remove a file extension
   - check if a file name has an extension
   - check if a file name has a particular extension (as word),
     or matches a particular grouping of extensions (as wordRe).
This commit is contained in:
Mark Olesen
2016-12-18 20:40:03 +01:00
parent b08dadff62
commit e379720053
5 changed files with 270 additions and 27 deletions

View File

@ -37,6 +37,7 @@ Description
#include "IOstreams.H"
#include "OSspecific.H"
#include "POSIX.H"
#include "Switch.H"
#include "etcFiles.H"
using namespace Foam;
@ -47,6 +48,7 @@ using namespace Foam;
int main(int argc, char *argv[])
{
argList::noParallel();
argList::addBoolOption("ext", "test handing of file extensions");
argList::addBoolOption("construct", "test constructors");
argList::addBoolOption("default", "reinstate default tests");
argList::addNote("runs default tests or specified ones only");
@ -108,6 +110,131 @@ int main(int argc, char *argv[])
}
// Test various ext() methods
if (args.optionFound("ext"))
{
Info<<nl << nl << "handling of fileName extension" << nl;
fileName empty;
fileName endWithDot("some.path/name.");
fileName endWithSlash("some.path/");
fileName input0("some.file/with.out/extension");
fileName input1("path.to/media/image.png");
Info<<"File : " << input0 << " ext: "
<< Switch(input0.hasExt())
<< " = " << input0.ext() << nl;
Info<<"File : " << input1 << " ext: "
<< Switch(input1.hasExt())
<< " = " << input1.ext() << nl;
Info<<"File : " << endWithDot << " ext: "
<< Switch(endWithDot.hasExt())
<< " = " << endWithDot.ext() << " <-- perhaps return false?" << nl;
Info<<"File : " << endWithSlash << " ext: "
<< Switch(endWithSlash.hasExt())
<< " = " << endWithSlash.ext() << nl;
Info<<"Remove extension " << (input0.removeExt());
Info<< " now: " << input0 << nl;
Info<<"Remove extension " << (input1.removeExt());
Info<< " now: " << input1 << nl;
Info<<"Remove extension " << (endWithSlash.removeExt());
Info<< " now: " << endWithSlash << nl;
wordList exts{ "jpg", "png", "txt", word::null };
Info<<"Add extension(s): " << input1 << nl;
for (const word& e : exts)
{
Info<<"<" << e << "> -> " << input1.ext(e) << nl;
}
Info<< nl;
Info<<"Test hasExt(word)" << nl
<<"~~~~~~~~~~~~~~~~~" << nl;
Info<<"Has extension(s):" << nl
<< "input: " << input1 << nl;
for (const word& e : exts)
{
Info<<" '" << e << "' -> "
<< Switch(input1.hasExt(e)) << nl;
}
Info<< nl;
Info<<"Has extension(s):" << nl
<< "input: " << endWithDot << nl;
for (const word& e : exts)
{
Info<<" '" << e << "' -> "
<< Switch(endWithDot.hasExt(e)) << nl;
}
Info<< nl;
Info<<"Test hasExt(wordRe)" << nl
<<"~~~~~~~~~~~~~~~~~~~" << nl;
// A regex with a zero length matcher doesn't work at all:
// eg "(png|jpg|txt|)" regex matcher itself
wordRe matcher0("()", wordRe::REGEXP);
wordRe matcher1("(png|jpg|txt)", wordRe::REGEXP);
wordRe matcher2("(png|txt)", wordRe::REGEXP);
Info<<"Has extension(s):" << nl
<< "input: " << endWithDot << nl;
Info<<" " << matcher0 << " -> "
<< Switch(endWithDot.hasExt(matcher0)) << nl;
Info<<" " << matcher1 << " -> "
<< Switch(endWithDot.hasExt(matcher1)) << nl;
Info<<" " << matcher2 << " -> "
<< Switch(endWithDot.hasExt(matcher2)) << nl;
Info<< "input: " << input1 << nl;
Info<<" " << matcher0 << " -> "
<< Switch(input1.hasExt(matcher0)) << nl;
Info<<" " << matcher1 << " -> "
<< Switch(input1.hasExt(matcher1)) << nl;
Info<<" " << matcher2 << " -> "
<< Switch(input1.hasExt(matcher2)) << nl;
Info<< nl;
Info<<"Remove extension(s):" << nl << "input: " << input1 << nl;
while (!input1.empty())
{
if (input1.removeExt())
{
Info<< " -> " << input1 << nl;
}
else
{
Info<< "stop> " << input1 << nl;
break;
}
}
Info<< nl;
input0.clear();
Info<<"test with zero-sized: " << input0 << nl;
Info<<"add extension: " << input0.ext("abc") << nl;
Info<< nl;
input0 = "this/";
Info<<"test add after slash: " << input0 << nl;
Info<<"add extension: " << input0.ext("abc")
<< " <-- avoids accidentally creating hidden files" << nl;
Info<< nl;
input0 = "this.file.";
Info<<"test after dot: " << input0 << nl;
Info<<"add extension: " << input0.ext("abc")
<< " <-- No check for repeated dots (user error!)" << nl;
Info<< nl;
}
if (!defaultTests)
{
return 0;

View File

@ -27,6 +27,7 @@ License
#include "wordList.H"
#include "DynamicList.H"
#include "OSspecific.H"
#include "wordRe.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -303,9 +304,9 @@ Foam::fileName Foam::fileName::path() const
Foam::fileName Foam::fileName::lessExt() const
{
size_type i = find_last_of("./");
size_type i = find_ext();
if (i == npos || i == 0 || operator[](i) == '/')
if (i == npos)
{
return *this;
}
@ -318,9 +319,9 @@ Foam::fileName Foam::fileName::lessExt() const
Foam::word Foam::fileName::ext() const
{
size_type i = find_last_of("./");
size_type i = find_ext();
if (i == npos || i == 0 || operator[](i) == '/')
if (i == npos)
{
return word::null;
}
@ -331,6 +332,71 @@ Foam::word Foam::fileName::ext() const
}
Foam::fileName& Foam::fileName::ext(const word& ending)
{
if (!ending.empty() && !empty() && operator[](size()-1) != '/')
{
append(".");
append(ending);
}
return *this;
}
bool Foam::fileName::hasExt() const
{
return (find_ext() != npos);
}
bool Foam::fileName::hasExt(const word& ending) const
{
size_type i = find_ext();
if (i == npos)
{
return false;
}
++i; // Do next comparison *after* the dot
return
(
// Lengths must match
((size() - i) == ending.size())
&& !compare(i, npos, ending)
);
}
bool Foam::fileName::hasExt(const wordRe& ending) const
{
size_type i = find_ext();
if (i == npos)
{
return false;
}
std::string end = substr(i+1, npos);
return ending.match(end);
}
bool Foam::fileName::removeExt()
{
const size_type i = find_ext();
if (i == npos)
{
return false;
}
else
{
this->resize(i);
return true;
}
}
Foam::wordList Foam::fileName::components(const char delimiter) const
{
DynamicList<word> wrdList(20);

View File

@ -57,6 +57,7 @@ template<class T> class UList;
typedef List<word> wordList;
// Forward declaration of friend functions and operators
class wordRe;
class fileName;
Istream& operator>>(Istream&, fileName&);
@ -73,6 +74,10 @@ class fileName
{
// Private Member Functions
//- Find position of the file extension dot, return npos on failure.
// A wrapped version of find_last_of("./") with additional logic.
inline size_type find_ext() const;
//- Strip invalid characters
inline void stripInvalid();
@ -104,35 +109,35 @@ public:
inline fileName();
//- Construct as copy
inline fileName(const fileName&);
inline fileName(const fileName& fn);
//- Construct as copy of word
inline fileName(const word&);
inline fileName(const word& s);
//- Construct as copy of string
inline fileName(const string&, const bool doStripInvalid=true);
inline fileName(const string& s, const bool doStripInvalid=true);
//- Construct as copy of std::string
inline fileName(const std::string&, const bool doStripInvalid=true);
inline fileName(const std::string& s, const bool doStripInvalid=true);
//- Construct as copy of character array
inline fileName(const char*, const bool doStripInvalid=true);
inline fileName(const char* s, const bool doStripInvalid=true);
//- Construct by concatenating elements of wordList separated by '/'
explicit fileName(const UList<word>&);
explicit fileName(const UList<word>& lst);
//- Construct by concatenating words separated by '/'
explicit fileName(std::initializer_list<word>);
explicit fileName(std::initializer_list<word> lst);
//- Construct from Istream
fileName(Istream&);
fileName(Istream& is);
// Member functions
//- Is this character valid for a fileName?
inline static bool valid(char);
inline static bool valid(char c);
//- Cleanup file name
//
@ -209,6 +214,24 @@ public:
//- Return file name extension (part after last .)
word ext() const;
//- Append a '.' and the ending, and return the object.
// The '.' and ending will not be added when the ending is empty,
// or when the file name is empty or ended with a '/'.
fileName& ext(const word& ending);
//- Return true if it has an extension or simply ends with a '.'
bool hasExt() const;
//- Return true if the extension is the same as the given ending.
bool hasExt(const word& ending) const;
//- Return true if the extension matches the given ending.
bool hasExt(const wordRe& ending) const;
//- Remove extension, returning true if string changed.
bool removeExt();
//- Return path components as wordList
//
// Behaviour:
@ -222,29 +245,43 @@ public:
wordList components(const char delimiter = '/') const;
//- Return a single component of the path
word component(const size_type, const char delimiter='/') const;
word component
(
const size_type cmpt,
const char delimiter = '/'
) const;
// Member operators
// Assignment
void operator=(const fileName&);
void operator=(const word&);
void operator=(const string&);
void operator=(const std::string&);
void operator=(const char*);
//- Copy, no character validation required
void operator=(const fileName& str);
//- Copy, no character validation required
void operator=(const word& str);
//- Copy, stripping invalid characters
void operator=(const string& str);
//- Copy, stripping invalid characters
void operator=(const std::string& str);
//- Copy, stripping invalid characters
void operator=(const char* str);
// IOstream operators
friend Istream& operator>>(Istream&, fileName&);
friend Ostream& operator<<(Ostream&, const fileName&);
friend Istream& operator>>(Istream& is, fileName& fn);
friend Ostream& operator<<(Ostream& os, const fileName& fn);
};
//- Assemble words and fileNames as pathnames by adding a '/' separator
fileName operator/(const string&, const string&);
//- Assemble words and fileNames as pathnames by adding a '/' separator.
// No '/' separator is added if either argument is an empty string.
fileName operator/(const string& a, const string& b);
//- Recursively search the given directory for the file

View File

@ -25,6 +25,21 @@ License
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
inline std::string::size_type Foam::fileName::find_ext() const
{
const size_type i = find_last_of("./");
if (i == npos || i == 0 || operator[](i) == '/')
{
return npos;
}
else
{
return i;
}
}
inline void Foam::fileName::stripInvalid()
{
// skip stripping unless debug is active to avoid

View File

@ -78,5 +78,3 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const fileName& fn)
// ************************************************************************* //