可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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());