C++使用strcpy和strcmp从文件读数据排序数组时大小写分离问题
解决C++排序时姓氏大小写导致的分组问题
这个问题我太熟了——strcmp是严格按照ASCII码值来比较的,而大写字母的ASCII码(65-90)全部小于小写字母(97-122),所以它会把所有首字母大写的姓氏一股脑排在小写开头的前面,完全不管实际的字典顺序。要解决这个问题,核心就是让排序时忽略大小写差异。
下面给你两种实用的解决方案,你可以根据自己的代码风格选:
1. 自定义不区分大小写的字符串比较函数
如果你想继续用C风格的char数组,自己实现一个忽略大小写的strcmp变体是最直接的:
#include <cctype> // 忽略大小写的字符串比较函数 int strcmpIgnoreCase(const char* str1, const char* str2) { while (*str1 != '\0' && *str2 != '\0') { // 转成unsigned char避免有符号char的未定义行为 unsigned char c1 = static_cast<unsigned char>(*str1); unsigned char c2 = static_cast<unsigned char>(*str2); char lowerC1 = tolower(c1); char lowerC2 = tolower(c2); if (lowerC1 != lowerC2) { return lowerC1 - lowerC2; } str1++; str2++; } // 处理其中一个字符串先结束的情况(比如"Smith"和"Smithson") unsigned char c1 = static_cast<unsigned char>(*str1); unsigned char c2 = static_cast<unsigned char>(*str2); return tolower(c1) - tolower(c2); }
然后把你排序逻辑里的strcmp替换成这个函数就行。比如假设你用冒泡排序:
struct Customer { char lastName[50]; char firstName[50]; int daysOfRental; double balanceDue; }; void sortCustomers(Customer customers[], int total) { for (int i = 0; i < total - 1; i++) { for (int j = 0; j < total - i - 1; j++) { // 替换成自定义的忽略大小写比较 if (strcmpIgnoreCase(customers[j].lastName, customers[j+1].lastName) > 0) { // 直接交换整个结构体,比逐个strcpy更高效 Customer temp = customers[j]; customers[j] = customers[j+1]; customers[j+1] = temp; } } } }
2. 用C++标准库的字符串和排序工具(更现代的写法)
如果可以改用std::string来存储姓名,代码会更简洁易读,还能利用C++标准库的强大功能:
#include <algorithm> #include <cctype> #include <string> #include <vector> struct Customer { std::string lastName; std::string firstName; int daysOfRental; double balanceDue; }; // 自定义比较器:忽略大小写比较姓氏 bool compareByLastNameIgnoreCase(const Customer& a, const Customer& b) { std::string aLower = a.lastName; std::string bLower = b.lastName; // 把两个姓氏都转成小写 std::transform(aLower.begin(), aLower.end(), aLower.begin(), [](unsigned char c) { return tolower(c); }); std::transform(bLower.begin(), bLower.end(), bLower.begin(), [](unsigned char c) { return tolower(c); }); return aLower < bLower; } // 排序时直接用std::sort void sortCustomers(std::vector<Customer>& customers) { std::sort(customers.begin(), customers.end(), compareByLastNameIgnoreCase); }
这样处理后,你的示例里的anilo会和Anderson按实际字典序排在一起,smitty会紧跟在Smith后面,xu也不会跑到最后去了。
小提醒
不管用哪种方法,都要注意tolower的参数——一定要转成unsigned char,因为如果你的char是有符号类型,某些扩展ASCII字符(比如0x80以上)会被当成负数,传入tolower会导致未定义行为。
内容的提问来源于stack exchange,提问作者Latusken
相关产品推荐
相关产品推荐

