有时候我们需要对字符串进行排序,如果该字符串只包含数字或者只包含字母时,都有比较直接的排序,直接调用std::sort
方法就可以完成排序。
但是如果我们字符串既包含数字,又包含字母时,例如对包含数字或字母的文件名字符串进行排序时,一般我们希望首先按照字符串开头数字的大小进行排序,然后按照字母的顺序进行排序,有点像是我们操作系统文件浏览器查看文件时的一种排序,这也叫做自然排序。这在C++中如何实现呢?
C++中并没有现成的函数能够完成此操作,需要自己进行编写,代码如下:
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
可以看出,输出结果比较符合我们想要的自然排序规则。
【参考】
1、https://stackoverflow.com/questions/9743485/natural-sort-of-directory-filenames-in-c