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

时间:2012-12-13 09:39:58

标签: c++ c sorting

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

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

enter image description here

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

enter image description here

如何按这种方式排序?请帮忙

6 个答案:

答案 0 :(得分:7)

对于C答案,以下内容是strcasecmp()的替代。此函数recurses处理包含交替数字和非数字子字符串的字符串。您可以将其与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()
  • 如果数字真的大,上面的代码(显然)会给出不正确的结果。
  • 此处忽略前导零。在我的区域,这是一个功能,而不是一个错误:我们通常希望UAL0123匹配UAL123。但这可能是您要求的也可能不是。
  • 另请参阅Sort on a string that may contain a numberHow to implement a natural sort algorithm in c++?,尽管与上述代码相比,那里或其链接中的答案肯定是漫长而漫无目的的,大约至少四倍。

答案 1 :(得分:6)

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

    #ifndef JSW_NATURAL_COMPARE
    #define JSW_NATURAL_COMPARE
    #include <string>
    int natural_compare(const char *a, const char *b);
    int natural_compare(const std::string& a, const std::string& b);
    #endif
    #include <cctype>
    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 <string>
    int natural_compare(const std::string& a, const std::string& b)
    {
      return natural_compare(a.c_str(), b.c_str());
    }

答案 2 :(得分:5)

您要做的是执行“自然排序”。 Here is a blog post关于它,我相信在python中解释实现。 Here是一个完成它的perl模块。 How to implement a natural sort algorithm in c++?

似乎也有类似的问题

答案 3 :(得分:5)

考虑到它有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<string>& data)
{
    std::sort(data.begin(), data.end(), natural_less);
}

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

答案 4 :(得分:3)

修改this回答:

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 <iostream> // std::cout
#include <string>
#include <algorithm> // std::sort, std::copy
#include <iterator> // std::ostream_iterator
#include <sstream> // std::istringstream
#include <vector>
#include <cctype> // std::isdigit

int main()
{
    std::vector<std::string> 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::string>(std::cout, "\n"));
}

答案 5 :(得分:0)

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

在字典顺序中,Slide1位于Slide10之前Slide5之前。

您希望在Slide5之前Slide10,因为您对子字符串510(作为整数)有解释。

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

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

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

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