ENH: allow passing of comparator to sortToc methods

- this increases the flexibility of the interface

- Add stringOps 'natural' string sorting comparison.
  Digits are sorted in their natural order, which means that
      (file10.txt file05.txt file2.txt)
  are sorted as
      (file2.txt file05.txt file10.txt)

STYLE: consistent naming of template parameters for comparators

  - Compare for normal binary predicates
  - ListComparePredicate for list compare binary predicates
This commit is contained in:
Mark Olesen
2017-10-27 14:28:00 +02:00
parent 8ec64d8128
commit 0a62fd2f87
18 changed files with 757 additions and 79 deletions

View File

@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -27,6 +27,8 @@ Description
#include "SortableList.H" #include "SortableList.H"
#include "ListOps.H" #include "ListOps.H"
#include "HashSet.H"
#include "stringOps.H"
using namespace Foam; using namespace Foam;
@ -175,6 +177,142 @@ int main(int argc, char *argv[])
Info<< "flat = " << values << endl; Info<< "flat = " << values << endl;
} }
// Sort strings
{
HashSet<string> hashed
{
"2.txt",
"05.txt",
"15.txt",
"other.bak04",
"other.bak1",
"file1.txt",
"file10.txt",
"file2.txt",
"file100.txt",
"file.txt",
"file011.txt",
"file15.txt",
"file0009.txt",
"abcd.txt",
// Some regular processor directories
"processor0",
"processor1",
"processor9",
"processor10",
"processor11",
"processor20",
"processor21",
"processor35",
"processors",
// Aggregate processor directories
"processor0-31",
"processor32-63",
"processor64-95",
"processor96-127",
"processor128-159",
"processor160-191",
"processor192-223",
"processor224-255",
};
Info<< nl << "Test string sorting" << nl << endl;
// Using hash toc
if (true)
{
Info<< "Unsorted" << hashed.toc() << endl;
Info<< "sortedToc" << hashed.sortedToc() << endl;
Info<< "natural"
<< hashed.sortedToc(stringOps::natural_sort()) << endl;
Info<< "reverse natural"
<< hashed.sortedToc(stringOps::natural_sort::reverse())
<< endl;
}
// Normal list
if (true)
{
labelList order;
List<string> strings(hashed.toc());
Info<< nl << "stringList:" << strings << endl;
sort(strings);
Info<< "normal sort:" << strings << endl;
shuffle(strings);
sort(strings, stringOps::natural_sort());
Info<< "natural sort:" << strings << endl;
shuffle(strings);
sort(strings, stringOps::natural_sort::reverse());
Info<< "reverse natural:" << strings << endl;
strings = hashed.toc();
Info<< nl << "test sorted order" << endl;
Info<< nl << "list:" << strings << endl;
sortedOrder(strings, order);
Info<< "sortedOrder:" << flatOutput(order) << endl;
shuffle(strings);
sort(strings, stringOps::natural_sort());
Info<< "reverse natural:" << strings << endl;
shuffle(strings);
sort(strings, stringOps::natural_sort::reverse());
Info<< "reverse natural:" << strings << endl;
sortedOrder
(
strings,
order,
stringOps::natural_sort::less<string>(strings)
);
Info<< "natural sortedOrder: " << flatOutput(order) << endl;
}
// SortableList
if (false)
{
SortableList<string> sortable;
Info<< nl << "Testing sortable list";
// Assign to ensure list is initially unsorted
sortable = hashed.toc();
Info<< nl << "input:" << sortable << endl;
sortable.sort();
Info<< nl << "normal:" << sortable << endl;
// This is still a bother (looks fairly ugly)
// so not implemented for now
/// // Assign to ensure list is initially unsorted
/// sortable = hashed.toc();
/// sortable.sort
/// (
/// stringOps::natural_sort::less<string>(sortable)
/// );
/// Info<< nl << "natural:" << sortable << endl;
/// // Assign to ensure list is initially unsorted
/// sortable = hashed.toc();
/// sortable.sort
/// (
/// stringOps::natural_sort::greater<string>(sortable)
/// );
/// Info<< nl << "natural:" << sortable << endl;
}
}
Info<< "\nEnd\n" << endl; Info<< "\nEnd\n" << endl;
return 0; return 0;

View File

@ -114,6 +114,7 @@ $(strings)/wordRe/wordRe.C
$(strings)/wordRes/wordRes.C $(strings)/wordRes/wordRes.C
$(strings)/lists/hashedWordList.C $(strings)/lists/hashedWordList.C
$(strings)/stringOps/stringOps.C $(strings)/stringOps/stringOps.C
$(strings)/stringOps/stringOpsSort.C
$(strings)/parsing/parsing.C $(strings)/parsing/parsing.C
ops = primitives/ops ops = primitives/ops

View File

@ -189,6 +189,17 @@ Foam::wordList Foam::DictionaryBase<IDLListType, T>::sortedToc() const
} }
template<class IDLListType, class T>
template<class Compare>
Foam::wordList Foam::DictionaryBase<IDLListType, T>::sortedToc
(
const Compare& comp
) const
{
return hashedTs_.sortedToc(comp);
}
template<class IDLListType, class T> template<class IDLListType, class T>
void Foam::DictionaryBase<IDLListType, T>::insert(const word& keyword, T* tPtr) void Foam::DictionaryBase<IDLListType, T>::insert(const word& keyword, T* tPtr)
{ {

View File

@ -131,6 +131,10 @@ public:
//- Return the table of contents as a sorted list //- Return the table of contents as a sorted list
wordList sortedToc() const; wordList sortedToc() const;
//- Return table of contents sorted using the specified comparator
template<class Compare>
wordList sortedToc(const Compare& comp) const;
// Editing // Editing

View File

@ -268,13 +268,27 @@ Foam::List<Key> Foam::HashTable<T, Key, Hash>::toc() const
template<class T, class Key, class Hash> template<class T, class Key, class Hash>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc() const Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc() const
{ {
List<Key> keyLst = this->toc(); List<Key> keyLst(this->toc());
Foam::sort(keyLst); Foam::sort(keyLst);
return keyLst; return keyLst;
} }
template<class T, class Key, class Hash>
template<class Compare>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc
(
const Compare& comp
) const
{
List<Key> keyLst(this->toc());
Foam::sort(keyLst, comp);
return keyLst;
}
template<class T, class Key, class Hash> template<class T, class Key, class Hash>
template<class UnaryPredicate> template<class UnaryPredicate>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocKeys Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocKeys

View File

@ -347,6 +347,10 @@ public:
//- Return the table of contents as a sorted list //- Return the table of contents as a sorted list
List<Key> sortedToc() const; List<Key> sortedToc() const;
//- Return table of contents sorted using the specified comparator
template<class Compare>
List<Key> sortedToc(const Compare& comp) const;
//- Return the sorted table of contents with keys that satisfy //- Return the sorted table of contents with keys that satisfy
// the unary predicate, optionally with inverted logic. // the unary predicate, optionally with inverted logic.
template<class UnaryPredicate> template<class UnaryPredicate>

View File

@ -110,24 +110,44 @@ void inplaceMapKey(const labelUList& oldToNew, Container& lst);
template<class T> template<class T>
void sortedOrder(const UList<T>& lst, labelList& order); void sortedOrder(const UList<T>& lst, labelList& order);
template<class T, class Cmp> //- Sort using specified list compare predicate
void sortedOrder(const UList<T>& lst, labelList& order, const Cmp& cmp); template<class T, class ListComparePredicate>
void sortedOrder
(
const UList<T>& lst,
labelList& order,
const ListComparePredicate& comp
);
//- Generate (sorted) indices corresponding to duplicate list values //- Generate (sorted) indices corresponding to duplicate list values
template<class T> template<class T>
void duplicateOrder(const UList<T>& lst, labelList& order); void duplicateOrder(const UList<T>& lst, labelList& order);
template<class T, class Cmp> //- Generate (sorted) indices corresponding to duplicate list values
void duplicateOrder(const UList<T>& lst, labelList& order, const Cmp& cmp); // sort using specified list compare predicate
template<class T, class ListComparePredicate>
void duplicateOrder
(
const UList<T>& lst,
labelList& order,
const ListComparePredicate& comp
);
//- Generate (sorted) indices corresponding to unique list values //- Generate (sorted) indices corresponding to unique list values
template<class T> template<class T>
void uniqueOrder(const UList<T>& lst, labelList& order); void uniqueOrder(const UList<T>& lst, labelList& order);
template<class T, class Cmp> //- Generate (sorted) indices corresponding to unique list values
void uniqueOrder(const UList<T>& lst, labelList& order, const Cmp& cmp); // sort using specified list compare predicate
template<class T, class ListComparePredicate>
void uniqueOrder
(
const UList<T>& lst,
labelList& order,
const ListComparePredicate& comp
);
//- Inplace sorting and removal of duplicates. //- Inplace sorting and removal of duplicates.
@ -137,8 +157,12 @@ void inplaceUniqueSort(ListType& lst);
//- Inplace sorting and removal of duplicates. //- Inplace sorting and removal of duplicates.
// Do not use FixedList for the input list, since it doesn't resize. // Do not use FixedList for the input list, since it doesn't resize.
template<class ListType, class Cmp> template<class ListType, class ListComparePredicate>
void inplaceUniqueSort(ListType& lst, const Cmp& cmp); void inplaceUniqueSort
(
ListType& lst,
const ListComparePredicate& comp
);
//- Extract elements of List when select is a certain value. //- Extract elements of List when select is a certain value.

View File

@ -211,27 +211,30 @@ void Foam::sortedOrder
} }
template<class T, class Cmp> template<class T, class ListComparePredicate>
void Foam::sortedOrder void Foam::sortedOrder
( (
const UList<T>& lst, const UList<T>& lst,
labelList& order, labelList& order,
const Cmp& cmp const ListComparePredicate& comp
) )
{ {
const label len = lst.size();
// list lengths must be identical // list lengths must be identical
if (order.size() != lst.size()) if (order.size() != len)
{ {
// avoid copying any elements, they are overwritten anyhow // Avoid copying any elements, they are overwritten anyhow
order.clear(); order.clear();
order.setSize(lst.size()); order.setSize(len);
} }
forAll(order, elemI) for (label i=0; i<len; ++i)
{ {
order[elemI] = elemI; order[i] = i; // identity
} }
Foam::stableSort(order, cmp);
Foam::stableSort(order, comp);
} }
@ -246,12 +249,12 @@ void Foam::duplicateOrder
} }
template<class T, class Cmp> template<class T, class ListComparePredicate>
void Foam::duplicateOrder void Foam::duplicateOrder
( (
const UList<T>& lst, const UList<T>& lst,
labelList& order, labelList& order,
const Cmp& cmp const ListComparePredicate& comp
) )
{ {
if (lst.size() < 2) if (lst.size() < 2)
@ -260,7 +263,7 @@ void Foam::duplicateOrder
return; return;
} }
sortedOrder(lst, order, cmp); sortedOrder(lst, order, comp);
const label last = (order.size()-1); const label last = (order.size()-1);
label n = 0; label n = 0;
@ -286,15 +289,15 @@ void Foam::uniqueOrder
} }
template<class T, class Cmp> template<class T, class ListComparePredicate>
void Foam::uniqueOrder void Foam::uniqueOrder
( (
const UList<T>& lst, const UList<T>& lst,
labelList& order, labelList& order,
const Cmp& cmp const ListComparePredicate& comp
) )
{ {
sortedOrder(lst, order, cmp); sortedOrder(lst, order, comp);
if (order.size() > 1) if (order.size() > 1)
{ {
@ -324,18 +327,24 @@ void Foam::inplaceUniqueSort(ListType& lst)
} }
template<class ListType, class Cmp> template<class ListType, class ListComparePredicate>
void Foam::inplaceUniqueSort(ListType& lst, const Cmp& cmp) void Foam::inplaceUniqueSort
(
ListType& lst,
const ListComparePredicate& comp
)
{ {
labelList order; labelList order;
uniqueOrder(lst, order, cmp); uniqueOrder(lst, order, comp);
ListType newLst(order.size()); const label len = order.size();
newLst.setSize(order.size()); // Consistent sizing (eg, DynamicList)
forAll(order, elemI) ListType newLst(len);
newLst.setSize(len); // Consistent sizing (eg, DynamicList)
for (label i=0; i<len; ++i)
{ {
newLst[elemI] = lst[order[elemI]]; newLst[i] = lst[order[i]];
} }
lst.transfer(newLst); lst.transfer(newLst);

View File

@ -133,7 +133,7 @@ Foam::List<T>& Foam::SortableList<T>::shrink()
template<class T> template<class T>
void Foam::SortableList<T>::sort() void Foam::SortableList<T>::sort()
{ {
sortedOrder(*this, indices_); Foam::sortedOrder(*this, indices_);
List<T> lst(*this, indices_); // Copy with indices for mapping List<T> lst(*this, indices_); // Copy with indices for mapping
List<T>::transfer(lst); List<T>::transfer(lst);
@ -143,7 +143,7 @@ void Foam::SortableList<T>::sort()
template<class T> template<class T>
void Foam::SortableList<T>::reverseSort() void Foam::SortableList<T>::reverseSort()
{ {
sortedOrder(*this, indices_, typename UList<T>::greater(*this)); Foam::sortedOrder(*this, indices_, typename UList<T>::greater(*this));
List<T> lst(*this, indices_); // Copy with indices for mapping List<T> lst(*this, indices_); // Copy with indices for mapping
List<T>::transfer(lst); List<T>::transfer(lst);
@ -157,6 +157,7 @@ void Foam::SortableList<T>::swap(SortableList<T>& lst)
indices_.swap(lst.indices_); indices_.swap(lst.indices_);
} }
template<class T> template<class T>
Foam::Xfer<Foam::List<T>> Foam::SortableList<T>::xfer() Foam::Xfer<Foam::List<T>> Foam::SortableList<T>::xfer()
{ {

View File

@ -119,8 +119,8 @@ public:
//- Clear the indices and return a reference to the underlying List //- Clear the indices and return a reference to the underlying List
List<T>& shrink(); List<T>& shrink();
//- (stable) sort the list (if changed after construction time) //- (stable) sort the list (if changed after construction time).
// also resizes the indices as required // Resizes the indices as required
void sort(); void sort();
//- Reverse (stable) sort the list //- Reverse (stable) sort the list

View File

@ -293,10 +293,10 @@ void Foam::sort(UList<T>& a)
} }
template<class T, class Cmp> template<class T, class Compare>
void Foam::sort(UList<T>& a, const Cmp& cmp) void Foam::sort(UList<T>& a, const Compare& comp)
{ {
std::sort(a.begin(), a.end(), cmp); std::sort(a.begin(), a.end(), comp);
} }
@ -307,10 +307,10 @@ void Foam::stableSort(UList<T>& a)
} }
template<class T, class Cmp> template<class T, class Compare>
void Foam::stableSort(UList<T>& a, const Cmp& cmp) void Foam::stableSort(UList<T>& a, const Compare& comp)
{ {
std::stable_sort(a.begin(), a.end(), cmp); std::stable_sort(a.begin(), a.end(), comp);
} }

View File

@ -139,39 +139,35 @@ public:
// Public classes // Public classes
//- Less function class that can be used for sorting //- A list compare binary predicate for normal sort
class less struct less
{ {
const UList<T>& values_; const UList<T>& values;
public: less(const UList<T>& list)
less(const UList<T>& values)
: :
values_(values) values(list)
{} {}
bool operator()(const label a, const label b) bool operator()(const label a, const label b) const
{ {
return values_[a] < values_[b]; return values[a] < values[b];
} }
}; };
//- Greater function class that can be used for sorting //- A list compare binary predicate for reverse sort
class greater struct greater
{ {
const UList<T>& values_; const UList<T>& values;
public: greater(const UList<T>& list)
greater(const UList<T>& values)
: :
values_(values) values(list)
{} {}
bool operator()(const label a, const label b) bool operator()(const label a, const label b) const
{ {
return values_[a] > values_[b]; return values[a] > values[b];
} }
}; };
@ -487,14 +483,14 @@ public:
template<class T> template<class T>
void sort(UList<T>& a); void sort(UList<T>& a);
template<class T, class Cmp> template<class T, class Compare>
void sort(UList<T>& a, const Cmp& cmp); void sort(UList<T>& a, const Compare& comp);
template<class T> template<class T>
void stableSort(UList<T>& a); void stableSort(UList<T>& a);
template<class T, class Cmp> template<class T, class Compare>
void stableSort(UList<T>& a, const Cmp& cmp); void stableSort(UList<T>& a, const Compare& comp);
template<class T> template<class T>
void shuffle(UList<T>& a); void shuffle(UList<T>& a);

View File

@ -452,6 +452,10 @@ public:
//- Return the sorted table of contents //- Return the sorted table of contents
wordList sortedToc() const; wordList sortedToc() const;
//- Return table of contents sorted using the specified comparator
template<class Compare>
wordList sortedToc(const Compare& comp) const;
//- Return the list of available keys or patterns //- Return the list of available keys or patterns
List<keyType> keys(bool patterns = false) const; List<keyType> keys(bool patterns = false) const;

View File

@ -28,6 +28,13 @@ License
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Compare>
Foam::wordList Foam::dictionary::sortedToc(const Compare& comp) const
{
return hashedEntries_.sortedToc(comp);
}
template<class T> template<class T>
T Foam::dictionary::lookupType T Foam::dictionary::lookupType
( (

View File

@ -49,37 +49,40 @@ namespace predicates
Class always Declaration Class always Declaration
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
//- Unary and binary predicates returning true, useful for templating. //- Unary and binary predicates that always return true, useful for templating.
class always struct always
{ {
public:
typedef always value_type; typedef always value_type;
//- Construct null //- Null constructible
inline always() inline always()
{} {}
//- Evaluated as a bool - return true //- Evaluated as a bool
// \return true
inline operator bool() const inline operator bool() const
{ {
return true; return true;
} }
//- Unary predicate returning true //- Unary predicate
// \return true
template<class T> template<class T>
inline bool operator()(const T&) const inline bool operator()(const T&) const
{ {
return true; return true;
} }
//- Binary predicate returning true //- Binary predicate
// \return true
template<class T1, class T2> template<class T1, class T2>
inline bool operator()(const T1&, const T2&) const inline bool operator()(const T1&, const T2&) const
{ {
return true; return true;
} }
//- String match returning true //- String match
// \return true
inline bool match(const std::string&, bool literal=false) const inline bool match(const std::string&, bool literal=false) const
{ {
return true; return true;
@ -91,37 +94,40 @@ public:
Class never Declaration Class never Declaration
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
//- Unary and binary predicates returning false, useful for templating. //- Unary and binary predicates that never return true, useful for templating.
class never struct never
{ {
public:
typedef never value_type; typedef never value_type;
//- Construct null //- Null constructible
inline never() inline never()
{} {}
//- Evaluated as a bool - return false //- Evaluated as a bool
// \return false
inline operator bool() const inline operator bool() const
{ {
return false; return false;
} }
//- Unary predicate returning false //- Unary predicate
// \return false
template<class T> template<class T>
inline bool operator()(const T&) const inline bool operator()(const T&) const
{ {
return false; return false;
} }
//- Binary predicate returning false //- Binary predicate
// \return false
template<class T1, class T2> template<class T1, class T2>
inline bool operator()(const T1&, const T2&) const inline bool operator()(const T1&, const T2&) const
{ {
return false; return false;
} }
//- String match returning false //- String match
// \return false
inline bool match(const std::string&, bool literal=false) const inline bool match(const std::string&, bool literal=false) const
{ {
return false; return false;

View File

@ -41,6 +41,7 @@ SourceFiles
#include "word.H" #include "word.H"
#include "dictionary.H" #include "dictionary.H"
#include "HashTable.H" #include "HashTable.H"
#include "stringOpsSort.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -0,0 +1,325 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
Changes for OpenFOAM
Code cleanup, reduction, elimination of redundant code.
\*---------------------------------------------------------------------------*/
//========================================================================
// Copyright (c) 1998-2010,2011 Free Software Foundation, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, distribute with modifications, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
// THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Except as contained in this notice, the name(s) of the above copyright
// holders shall not be used in advertising or otherwise to promote the
// sale, use or other dealings in this Software without prior written
// authorization.
//========================================================================
//========================================================================
// Original Author: Jan-Marten Spit <jmspit@euronet.nl>
//========================================================================
#include "stringOpsSort.H"
#include <cctype>
#include <cstring>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Local tweaks/preferences:
//
// [DIGITS_ALWAYS_FIRST] : as per original code
// This results in "file123.txt" sorting before "file.txt", which is
// inconsistent with what 'ls -v' produces
// - normally do not want this (Mark Olesen: Oct-2017)
#undef DIGITS_ALWAYS_FIRST
// [IGNORE_LEADING_ZEROS] : as per original code
// This results in "file0005.txt" sorting before "file06.txt"
// -> normally want this (Mark Olesen: Oct-2017)
#define IGNORE_LEADING_ZEROS
// [MANUAL_NUMCOMPARE] : handwritten code instead of strncmp
// The orignal code has a mix of strncmp for equality but handwritten code
// for greater-than/less-than.
//
// -> this does to be unneeded, rely on strncmp() return values
// (Mark Olesen: Oct-2017)
#undef MANUAL_NUMCOMPARE
// [DEBUG_NATSTRCMP] : debug info to std::cerr (for development purposes only)
#undef DEBUG_NATSTRCMP
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef DEBUG_NATSTRCMP
#include <iostream>
template<class T>
static inline void debugPrint(const char* text, T item1, T item2)
{
std::cerr << text << ": <" << item1 << "> <" << item2 << ">\n";
}
// Character sequences, end pointer is _inclusive_.
static inline void debugPrint
(
const char* b1, const char* e1,
const char* b2, const char* e2
)
{
std::cerr << "<";
while (b1 < e1) { std::cerr << *b1++; }
std::cerr << *e1 << "> ";
std::cerr << "<";
while (b2 < e2) { std::cerr << *b2++; }
std::cerr << *e2 << ">\n";
}
#endif
#ifdef MANUAL_NUMCOMPARE
// Manual comparison of identical length (digit) sequences
static inline int manual_numcompare(const char* s1, const char* s2)
{
while (*s1 && *s2)
{
const int cmp = (*s1 - *s2);
if (!cmp) return cmp;
++s1; ++s2;
}
return 0; // Length check done before in caller.
}
#endif
// ------------------------------------------------------------------------- //
int Foam::stringOps::natstrcmp(const char* s1, const char* s2)
{
#ifdef DEBUG_NATSTRCMP
debugPrint("natstrcmp", s1, s2);
#endif
// States for state engine
enum stateType { SCAN, ALPHA, NUMERIC };
// Number of leading zeroes
unsigned zeros1 = 0;
unsigned zeros2 = 0;
// Pointers to begin/end of integer sequences (without leading zeros)
const char* numbeg1 = nullptr;
const char* numbeg2 = nullptr;
const char* numend1 = nullptr;
const char* numend2 = nullptr;
stateType state = SCAN;
const char* p1 = s1;
const char* p2 = s2;
while (*p1 && *p2)
{
// Bitmask for digits vs alpha
// - 0: neither are digits
// - 1: p1 is the only digit
// - 2: p2 is the only digit
// - 3: both p1 and p2 are digits
const unsigned digitMask =
((isdigit(*p2) ? 2:0) | (isdigit(*p1) ? 1:0));
switch (state)
{
case SCAN:
{
#ifdef DEBUG_NATSTRCMP
debugPrint("SCAN", *p1, *p2);
#endif
switch (digitMask)
{
case 0: // (alpha,alpha)
{
state = ALPHA;
if (*p1 == *p2)
{
++p1; ++p2;
}
else
{
// Lexical compare
return (*p1 - *p2);
}
break;
}
#ifdef DIGITS_ALWAYS_FIRST
case 0x1: // (digit,alpha) : digit < alpha
{
return -1;
break;
}
case 0x2: // (alpha,digit) : alpha > digit
{
return 1;
break;
}
#else /* DIGITS_ALWAYS_FIRST */
case 0x1: // (digit,alpha)
case 0x2: // (alpha,digit)
{
// Lexical compare for digits/alpha
return (*p1 - *p2);
break;
}
#endif /* DIGITS_ALWAYS_FIRST */
default: // (digit,digit)
{
state = NUMERIC;
#ifdef IGNORE_LEADING_ZEROS
if (!zeros1) // Start skip of leading zeros
{
while (*p1 == '0') { ++p1; ++zeros1; }
}
else
#endif
{
while (*p1 == '0') { ++p1; }
}
#ifdef IGNORE_LEADING_ZEROS
if (!zeros2) // Start skip of leading zeros
{
while (*p2 == '0') { ++p2; ++zeros2; }
}
else
#endif
{
while (*p2 == '0') { ++p2; }
}
if (zeros1 == zeros2)
{
// Same number of zeros - so irrelevant
zeros1 = zeros2 = 0;
}
if (!isdigit(*p1)) --p1;
if (!isdigit(*p2)) --p2;
numbeg1 = numend1 = p1;
numbeg2 = numend2 = p2;
break;
}
}
break;
}
case ALPHA:
{
#ifdef DEBUG_NATSTRCMP
debugPrint("ALPHA", *p1, *p2);
#endif
if (digitMask)
{
state = SCAN;
}
else // (alpha,alpha)
{
if (*p1 == *p2)
{
++p1; ++p2;
}
else
{
// Lexical compare
return (*p1 - *p2);
}
}
break;
}
case NUMERIC:
{
while (isdigit(*p1)) numend1 = p1++;
while (isdigit(*p2)) numend2 = p2++;
#ifdef DEBUG_NATSTRCMP
debugPrint("NUMERIC", *p1, *p2);
debugPrint(numbeg1,numend1, numbeg2,numend2);
#endif
// Length (minus 1) of each sequence
const size_t len1 = (numend1 - numbeg1);
const size_t len2 = (numend2 - numbeg2);
if (len1 < len2)
{
return -1;
}
else if (len1 > len2)
{
return 1;
}
// Same number of digits, leading zeros have been skipped.
// - so lexical and numerical compares are equivalent.
const int cmp = strncmp(numbeg1, numbeg2, len1+1);
if (!cmp)
{
// Identical (digit) sequence - continue
state = SCAN;
}
else
{
#ifdef MANUAL_STRNCMP
return manual_numcompare(numbeg1, numbeg2);
#else
return cmp;
#endif
}
break;
}
}
}
if (zeros1 < zeros2) return -1;
if (zeros1 > zeros2) return 1;
if (!*p1 && *p2) return -1; // s1 shorter than s2
if (*p1 && !*p2) return 1; // s1 longer than s2
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,133 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
InNamespace
Foam::stringOps
Description
Specialized string sorting.
SourceFiles
stringOpsSort.C
\*---------------------------------------------------------------------------*/
#ifndef stringOpsSort_H
#define stringOpsSort_H
#include "stringOps.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace stringOps
{
//- 'Natural' compare for C-strings
// Uses algorithm and code from Jan-Marten Spit <jmspit@euronet.nl>
//
// In the 'natural' comparison, strings are compared alphabetically
// and numerically. Thus 'file010.txt' sorts after 'file2.txt'
//
// \param s1 left string
// \param s2 right string
// \return -1 when s1 < s2, 0 when s1 == s2, 1 when s1 > s2
int natstrcmp(const char* s1, const char* s2);
//- Encapsulation of natural order sorting for algorithms
struct natural_sort
{
//- Natural compare for std::string
// \return -1 when s1 < s2, 0 when s1 == s2, 1 when s1 > s2
static inline int compare
(
const std::string& s1,
const std::string& s2
)
{
return natstrcmp(s1.data(), s2.data());
}
//- Default (forward) natural sorting
bool operator()(const std::string& s1, const std::string& s2) const
{
return natural_sort::compare(s1, s2) < 0;
}
//- Reverse natural sorting
struct reverse
{
//- Reverse natural sorting
bool operator()(const std::string& s1, const std::string& s2) const
{
return natural_sort::compare(s1, s2) > 0;
}
};
//- A list compare binary predicate for natural sort
template<class T>
struct less
{
const UList<T>& values;
less(const UList<T>& list)
:
values(list)
{}
bool operator()(const label a, const label b) const
{
return natural_sort::compare(values[a], values[b]) < 0;
}
};
//- A list compare binary predicate for reverse natural sort
template<class T>
struct greater
{
const UList<T>& values;
greater(const UList<T>& list)
:
values(list)
{}
bool operator()(const label a, const label b) const
{
return natural_sort::compare(values[a], values[b]) > 0;
}
};
};
} // End namespace stringOps
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //