在C ++中自然sorting的目录文件名

我有一个目录列表,我想要检索文件名,并把它们放在一个string的向量中,以“自然”方式sorting。 例如{ "10.txt" "0.txt" "2.txt" "1.m" "Jan12" "July13.txt" "Nov25.txt" "Jane" "John" }应该是{"0.txt" "1.m" "2.txt" "10.txt" "Jan12" "July13.txt" "Nov25.txt" "Jane" "John" } 。 什么是最简单的方法来做到这一点?

(N)和文本(T)构成的一个string,使得...(N)(T)... ,然后对于...(N1)(T1)......(N2)(T2)...将是(N1<N2) (<) (T1<T2)其中(<)表示左边的优先权超过了右边的期限。 在这种情况下,如果数字在string中的位置相同,比如1.z (<) 1_t.txt ,则数字优先于文本字段。

是否已经有一个库函数来为字母数字string或目录条目进行这种sorting?

DESIRED文件应该来的顺序 。 文件名将被存储在一个string的vector中。

 Abhinav@Abhinav-PC /cygdrive/c/AbhinavSamples/shell $ ls -lv total 8 -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:51 1.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:55 1_t.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:50 3.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:51 4.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:53 10.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:56 10_t.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:56 13.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:53 20.txt **Simple Sort** Abhi@Abhi-PC /cygdrive/c/AbhinavSamples/shell $ ls -l total 8 -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:51 1.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:53 10.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:56 10_t.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:56 13.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:55 1_t.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:53 20.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:50 3.txt -rw-r--r--+ 1 Abhinav None 2 Mar 17 00:51 4.txt 

有一个函数完全符合你想要的glibc。 不幸的是,它是C而不是C ++,所以如果你能忍受,那么这里是最简单的解决方案,不用重新实现,重新发明轮子。 顺便说一句:这和ls -lv是一样的。 它最重要的部分是为你自然排序versionsort功能。 它在这里被用作scandir的比较函数。 下面的简单例子打印当前目录中的所有文件/目录。

 #define _GNU_SOURCE #include <dirent.h> #include <stdlib.h> #include <stdio.h> int main(void) { struct dirent **namelist; int n,i; n = scandir(".", &namelist, 0, versionsort); if (n < 0) perror("scandir"); else { for(i =0 ; i < n; ++i) { printf("%s\n", namelist[i]->d_name); free(namelist[i]); } free(namelist); } return 0; } 

你需要一个使两个字符串自然比较的函数。 之后,你可以使用std::sort作为第三个参数(作为@chac已经指出)。 在下面我试图以递归的方式实现这样一个功能。 注意,它可以处理任何不需要以数字部分开始并以字符串部分结束的文件名:

 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 (std::toupper(a[0]) == std::toupper(b[0])) return compareNat(a.substr(1), b.substr(1)); return (std::toupper(a[0]) < std::toupper(b[0])); } // 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)); } 

这是一个简单的测试用例:

 #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"); std::sort(str.begin(), str.end(), compareNat); std::copy(str.begin(), str.end(), std::ostream_iterator<std::string>(std::cout, "\n")); } 

结果是:

 1.txt 1_t.txt 10.txt 20.txt z2.txt z10.txt z100.txt 

你可以使用std :: sort,以数字+字符串(可选)分割文件名。

 #include <vector> #include <string> #include <algorithm> #include <iostream> #include <iterator> #include <cstring> using namespace std; bool compare_filenames(string a, string b) { char *pA, *pB; long A = strtol(a.c_str(), &pA, 10), B = strtol(b.c_str(), &pB, 10); if (A < B) return true; if (A == B) return strcmp(pA, pB); return false; } int main_compare_filenames(int, char **) { const char *v[] ={ "1.txt", "10.txt", "10_t.txt", "13.txt", "1_t.txt", "20.txt", "3.txt", "4.txt" }; vector<string> t(v, v + 8); sort(t.begin(), t.end(), compare_filenames); copy(t.begin(), t.end(), ostream_iterator<string>(cout, "\n")); return 0; } 

输出:

 1_t.txt 1.txt 3.txt 4.txt 10_t.txt 10.txt 13.txt 20.txt 

这几乎是有效的,但是有一个问题,就是“_”先于“。”,因此需要进一步的调整:

 string remove_dot(const char *p) { const char *dot = strchr(p, '.'); return dot ? string(p, dot - p) : string(p); } bool compare_filenames(string a, string b) { char *pA, *pB; long A = strtol(a.c_str(), &pA, 10), B = strtol(b.c_str(), &pB, 10); if (A < B) return true; if (A == B) return remove_dot(pA) < remove_dot(pB); return false; } 

输出:

 1.txt 1_t.txt 3.txt 4.txt 10.txt 10_t.txt 13.txt 20.txt 

我遇到了一个很好的算法: http : //sourcefrog.n​​et/projects/natsort/

我已经修改了一些信息来满足我的需求:

  1. 传递std::string作为参数。
  2. 使用std::sortstd::vector

我的源代码版本在这里 。

用例示例:

 #include <iostream> #include <vector> #include <algorithm> #include "strnatcmp.hpp" int main(){ std::vector<std::string> files; files.push_back("20.txt"); files.push_back("10.txt"); files.push_back("1.txt"); files.push_back("z2.txt"); files.push_back("z10.txt"); files.push_back("z100.txt"); files.push_back("1_t.txt "); files.push_back("ABc"); files.push_back("aBCd"); files.push_back("aBc"); files.push_back("aaa"); files.push_back("aBcd"); files.push_back("aaA"); std::sort(files.begin(),files.end(),compareNat); for(int i=0;i<(int)files.size();i++)std::cout<< files[i]+"\n"; return 0; } 

输出:

 1.txt 1_t.txt 10.txt 20.txt aaa aaA ABc aBc aBCd aBcd z2.txt z10.txt z100.txt 

这是strnatcmp.hpp标题。