c++ qt natural-sort

c++ - Ordenar los nombres de los archivos de forma natural con Qt



natural-sort (5)

Estoy leyendo un contenido de directorios usando QDir::entryList() . Los nombres de archivo dentro están estructurados así:

index_randomNumber.png

Los necesito ordenados por index , la forma en que el Explorador de Windows ordenaría los archivos para que yo obtenga

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

En lugar de lo que la clasificación por QDir::Name me da:

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

¿Existe una forma integrada en Qt para lograr esto y, de no ser así, cuál es el lugar adecuado para implementarlo?


Qt no admite la clasificación natural de forma nativa, pero puede implementarse con bastante facilidad. Por ejemplo, esto se puede usar para ordenar una 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(); } };

Entonces el uso es simplemente:

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



Sí, es posible.

Para hacer eso, necesita especificar el indicador LocaleAware al construir el QDir . objeto. El constructor es

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

También puedes usar

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


Si desea utilizar QCollator para ordenar las entradas de la lista de entradas devueltas por QDir::entryList , puede ordenar el resultado con 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; });

De acuerdo con el comentario de The Badger , QCollator también se puede usar directamente como un argumento para std::sort , reemplazando la lambda, por lo que la última línea se convierte en:

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


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(); }