Сортировать имена файлов естественно с Qt
Я читаю содержимое каталогов с помощью QDir::entryList()
. Имена файлов внутри структурированы следующим образом:
index_randomNumber.png
мне нужно их отсортировать по index
, как проводник Windows будет сортировать файлы, чтобы я получил
0_0815.png
1_4711.png
2_2063.png
...
вместо того, что сортировка по QDir::Name
дает мне:
0_0815.png
10000_6661.png
10001_7401.png
...
есть ли встроенный способ в Qt для достижения этого, и если нет, то в каком месте его реализовать?
5 ответов
если вы хотите использовать QCollator
для сортировки записей из списка записей, возвращаемых QDir::entryList
, вы можете сортировать результат с 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;
});
по данным Барсукс комментарием QCollator
также может использоваться непосредственно в качестве аргумента для std::sort
, заменяя лямбду, поэтому последняя строка становится:
std::sort(entryList.begin(), entryList.end(), collator);
Qt не имел естественной реализации сортировки до Qt 5.2, см. этот запрос функции.
с Qt 5.2 есть QCollator что позволяет естественную сортировку, когда цифровой режим это.
Да, это возможно.
для этого необходимо указать флаг LocaleAware при строительстве QDir
. объект. Конструктор
QDir(const QString & path, const QString & nameFilter, SortFlags sort = SortFlags( Name | IgnoreCase ), Filters filters = AllEntries)
вы также можете использовать
QDir dir;
dir.setSorting(QDir::LocaleAware);
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();
}
Qt не поддерживает естественную сортировку изначально, но ее можно довольно легко реализовать. Например, это можно использовать для сортировки 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();
}
};
тогда использование просто:
std::sort(stringList.begin(), stringList.end(), naturalSortCompare());