Sort filenames naturally with Qt

匿名 (未验证) 提交于 2019-12-03 02:23:02

问题:

I am reading a directories content using QDir::entryList(). The filenames within are structured like this:

index_randomNumber.png

I need them sorted by index, the way the Windows Explorer would sort the files so that I get

0_0815.png 1_4711.png 2_2063.png ...

instead of what the sorting by QDir::Name gives me:

0_0815.png 10000_6661.png 10001_7401.png ...

Is there a built-in way in Qt to achieve this and if not, what's the right place to implement it?

回答1:

If you want to use QCollator to sort entries from the list of entries returned by QDir::entryList, you can sort the result with std::sort():

dir.setFilter(QDir::Files | QDir::NoSymLinks); dir.setSorting(QDir::NoSort);  // will sort manually with std::sort  auto entryList = dir.entryList();  QCollator collator; collator.setNumericMode(true);  std::sort(     entryList.begin(),     entryList.end(),     [&collator](const QString &file1, const QString &file2)     {         return collator.compare(file1, file2) < 0;     });

According to The Badger's comment, QCollator can also be used directly as an argument to std::sort, replacing the lambda, so the last line becomes:

std::sort(entryList.begin(), entryList.end(), collator);


回答2:

Qt didn't have natural sort implementation until Qt 5.2, see this feature request.

Since Qt 5.2 there is QCollator which allows natural sort when numeric mode is enabled.



回答3:

Yes it is possible.

In order to do that you need to specify the flag LocaleAware when constructing the QDir. object. The constructor is

 QDir(const QString & path, const QString & nameFilter, SortFlags sort = SortFlags( Name | IgnoreCase ), Filters filters = AllEntries)

You can also use

QDir dir; dir.setSorting(QDir::LocaleAware);


回答4:

inline int findNumberPart(const QString& sIn) {   QString s = "";   int i = 0;   bool isNum = false;   while (i < sIn.length())   {     if (isNum)     {       if (!sIn[i].isNumber())         break;       s += sIn[i];     }     else     {       if (sIn[i].isNumber())         s += sIn[i];     }     ++i;   }   if (s == "")     return 0;   return s.toInt(); }  bool naturalSortCallback(const QString& s1, const QString& s2) {   int idx1 = findNumberPart(s1);   int idx2 = findNumberPart(s2);   return (idx1 < idx2); }  int main(int argc, char *argv[]) {   QCoreApplication a(argc, argv);    QDir dir(MYPATH);   QStringList list = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot);   qSort(list.begin(), list.end(), naturalSortCallback);   foreach(QString s, list)     qDebug() << s << endl;    return a.exec(); }


回答5:

Qt doesn't support natural sorting natively, but it can be quite easily implemented. For example, this can be used to sort a QStringList:

struct naturalSortCompare {      inline bool isNumber(QChar c) {         return c >= '0' && c <= '9';     }      inline bool operator() (const QString& s1, const QString& s2) {         if (s1 == "" || s2 == "") return s1 < s2;          // Move to the first difference between the strings         int startIndex = -1;         int length = s1.length() > s2.length() ? s2.length() : s1.length();         for (int i = 0; i < length; i++) {             QChar c1 = s1[i];             QChar c2 = s2[i];             if (c1 != c2) {                 startIndex = i;                 break;             }         }          // If the strings are the same, exit now.         if (startIndex < 0) return s1 < s2;          // Now extract the numbers, if any, from the two strings.         QString sn1;         QString sn2;         bool done1 = false;         bool done2 = false;         length = s1.length() < s2.length() ? s2.length() : s1.length();          for (int i = startIndex; i < length; i++) {             if (!done1 && i < s1.length()) {                 if (isNumber(s1[i])) {                     sn1 += QString(s1[i]);                 } else {                     done1 = true;                 }             }              if (!done2 && i < s2.length()) {                 if (isNumber(s2[i])) {                     sn2 += QString(s2[i]);                 } else {                     done2 = true;                 }             }              if (done1 && done2) break;         }          // If none of the strings contain a number, use a regular comparison.         if (sn1 == "" && sn2 == "") return s1 < s2;          // If one of the strings doesn't contain a number at that position,         // we put the string without number first so that, for example,         // "example.bin" is before "example1.bin"         if (sn1 == "" && sn2 != "") return true;         if (sn1 != "" && sn2 == "") return false;          return sn1.toInt() < sn2.toInt();     }  };

Then usage is simply:

std::sort(stringList.begin(), stringList.end(), naturalSortCompare());


转载请标明出处:Sort filenames naturally with Qt
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!