如何在C中按顺序对数字和字母排序文件名?

我使用以下代码按字母顺序对文件进行排序,并对文件进行排序,如图所示:

for(int i = 0;i < maxcnt;i++) { for(int j = i+1;j  0) { strcpy(temp,Array[i]); strcpy(Array[i],Array[j]); strcpy(Array[j],temp); } } } 

在此处输入图像描述

但我需要按照Windows资源管理器中的顺序对其进行排序

在此处输入图像描述

怎么这样排序? 请帮忙

对于C答案,以下是strcasecmp()的替代。 此函数递归以处理包含交替数字和非数字子字符串的字符串。 您可以将它与qsort()

 int strcasecmp_withNumbers(const void *void_a, const void *void_b) { const char *a = void_a; const char *b = void_b; if (!a || !b) { // if one doesn't exist, other wins by default return a ? 1 : b ? -1 : 0; } if (isdigit(*a) && isdigit(*b)) { // if both start with numbers char *remainderA; char *remainderB; long valA = strtol(a, &remainderA, 10); long valB = strtol(b, &remainderB, 10); if (valA != valB) return valA - valB; // if you wish 7 == 007, comment out the next two lines else if (remainderB - b != remainderA - a) // equal with diff lengths return (remainderB - b) - (remainderA - a); // set 007 before 7 else // if numerical parts equal, recurse return strcasecmp_withNumbers(remainderA, remainderB); } if (isdigit(*a) || isdigit(*b)) { // if just one is a number return isdigit(*a) ? -1 : 1; // numbers always come first } while (*a && *b) { // non-numeric characters if (isdigit(*a) || isdigit(*b)) return strcasecmp_withNumbers(a, b); // recurse if (tolower(*a) != tolower(*b)) return tolower(*a) - tolower(*b); a++; b++; } return *a ? 1 : *b ? -1 : 0; } 

笔记:

  • Windows需要stricmp()而不是Unix等效的strcasecmp()
  • 如果数字非常大,上面的代码(显然)会给出不正确的结果。
  • 这里忽略前导零。 在我的区域,这是一个function,而不是一个错误:我们通常希望UAL0123匹配UAL123。 但这可能是也可能不是你所需要的。
  • 另请参阅对可能包含数字的字符串进行排序以及如何在c ++中实现自然排序算法? 虽然那里的答案,或者在它们的链接中,与上面的代码相比,肯定是漫长而漫无目的的,大约至少四倍。

你想要做的是执行“自然排序”。 这是一篇关于它的博客文章 ,我相信在python中解释实现。 这是一个完成它的perl模块。 在如何在c ++中实现自然排序算法似乎也有类似的问题?

自然分拣是你必须在这里采取的方式。 我的方案有一个工作代码。 你可以根据自己的需要改变它来利用它:

  #ifndef JSW_NATURAL_COMPARE #define JSW_NATURAL_COMPARE #include  int natural_compare(const char *a, const char *b); int natural_compare(const std::string& a, const std::string& b); #endif #include  namespace { // Note: This is a convenience for the natural_compare // function, it is *not* designed for general use class int_span { int _ws; int _zeros; const char *_value; const char *_end; public: int_span(const char *src) { const char *start = src; // Save and skip leading whitespace while (std::isspace(*(unsigned char*)src)) ++src; _ws = src - start; // Save and skip leading zeros start = src; while (*src == '0') ++src; _zeros = src - start; // Save the edges of the value _value = src; while (std::isdigit(*(unsigned char*)src)) ++src; _end = src; } bool is_int() const { return _value != _end; } const char *value() const { return _value; } int whitespace() const { return _ws; } int zeros() const { return _zeros; } int digits() const { return _end - _value; } int non_value() const { return whitespace() + zeros(); } }; inline int safe_compare(int a, int b) { return a < b ? -1 : a > b; } } int natural_compare(const char *a, const char *b) { int cmp = 0; while (cmp == 0 && *a != '\0' && *b != '\0') { int_span lhs(a), rhs(b); if (lhs.is_int() && rhs.is_int()) { if (lhs.digits() != rhs.digits()) { // For differing widths (excluding leading characters), // the value with fewer digits takes priority cmp = safe_compare(lhs.digits(), rhs.digits()); } else { int digits = lhs.digits(); a = lhs.value(); b = rhs.value(); // For matching widths (excluding leading characters), // search from MSD to LSD for the larger value while (--digits >= 0 && cmp == 0) cmp = safe_compare(*a++, *b++); } if (cmp == 0) { // If the values are equal, we need a tie // breaker using leading whitespace and zeros if (lhs.non_value() != rhs.non_value()) { // For differing widths of combined whitespace and // leading zeros, the smaller width takes priority cmp = safe_compare(lhs.non_value(), rhs.non_value()); } else { // For matching widths of combined whitespace // and leading zeros, more whitespace takes priority cmp = safe_compare(rhs.whitespace(), lhs.whitespace()); } } } else { // No special logic unless both spans are integers cmp = safe_compare(*a++, *b++); } } // All else being equal so far, the shorter string takes priority return cmp == 0 ? safe_compare(*a, *b) : cmp; } #include  int natural_compare(const std::string& a, const std::string& b) { return natural_compare(a.c_str(), b.c_str()); } 

考虑到它有一个c++标签,你可以详细说明@Joseph Quinsey的答案并创建一个natural_less功的函数传递给标准库。

 using namespace std; bool natural_less(const string& lhs, const string& rhs) { return strcasecmp_withNumbers(lhs.c_str(), rhs.c_str()) < 0; } void example(vector& data) { std::sort(data.begin(), data.end(), natural_less); } 

我花时间写了一些工作代码作为练习https://github.com/kennethlaskoski/natural_less

修改这个答案:

 bool compareNat(const std::string& a, const std::string& b){ if (a.empty()) return true; if (b.empty()) return false; if (std::isdigit(a[0]) && !std::isdigit(b[0])) return true; if (!std::isdigit(a[0]) && std::isdigit(b[0])) return false; if (!std::isdigit(a[0]) && !std::isdigit(b[0])) { if (a[0] == b[0]) return compareNat(a.substr(1), b.substr(1)); return (toUpper(a) < toUpper(b)); //toUpper() is a function to convert a std::string to uppercase. } // Both strings begin with digit --> parse both numbers std::istringstream issa(a); std::istringstream issb(b); int ia, ib; issa >> ia; issb >> ib; if (ia != ib) return ia < ib; // Numbers are the same --> remove numbers and recurse std::string anew, bnew; std::getline(issa, anew); std::getline(issb, bnew); return (compareNat(anew, bnew)); } 

toUpper()函数:

 std::string toUpper(std::string s){ for(int i=0;i<(int)s.length();i++){s[i]=toupper(s[i]);} return s; } 

用法:

 #include  // std::cout #include  #include  // std::sort, std::copy #include  // std::ostream_iterator #include  // std::istringstream #include  #include  // std::isdigit int main() { std::vector str; str.push_back("20.txt"); str.push_back("10.txt"); str.push_back("1.txt"); str.push_back("z2.txt"); str.push_back("z10.txt"); str.push_back("z100.txt"); str.push_back("1_t.txt"); str.push_back("abc.txt"); str.push_back("Abc.txt"); str.push_back("bcd.txt"); std::sort(str.begin(), str.end(), compareNat); std::copy(str.begin(), str.end(), std::ostream_iterator(std::cout, "\n")); } 

您的问题是您在文件名的部分后面有一个解释。

在字典顺序中, Slide1Slide5之前的Slide5之前。

你期望Slide5之前的Slide5 ,因为你有一个子串510的解释(作为整数)。

如果你在文件名中有月份的名称,你会遇到更多的问题,并期望它们按日期排序(即1月份在8月之前)。 您将需要根据此解释调整您的排序(“自然”顺序将取决于您的解释,没有通用解决方案)。

另一种方法是以排序和字典顺序一致的方式格式化文件名。 在您的情况下,您将使用前导零和固定长度的数字。 所以Slide1变成了Slide01 ,然后你会看到按字典顺序排序它们会产生你想要的结果。

但是,通常您无法影响应用程序的输出,因此无法直接强制执行您的格式。

在这些情况下我做了什么:编写一个小脚本/函数,将文件重命名为适当的格式,然后使用标准排序算法对它们进行排序。 这样做的好处是您不需要调整排序,可以使用现有的软件进行排序。 在缺点方面,有些情况下这是不可行的(因为文件名需要修复)。