使用std :: sort对字符串进行排序,以便大写字母位于小写字母之后

时间:2013-10-22 04:41:56

标签: c++ string sorting

我想对矢量进行排序,以便大写字母遵循小写字母。如果我有像

这样的东西
This is a test
this is a test
Cats
cats
this thing

我希望输出为

cats
Cats
this is a test
This is a test
this thing

标准库排序将输出

Cats
This is a test
cats
this is a test
this thing

我想将谓词传递给std :: sort,以便将我传递的字符串的小写版本作为参数进行比较。

bool compare(std::string x, std::string y)
{
    return lowercase(x) < lowercase(y);
}

我尝试降低函数中的每个字符,然后进行比较,但它不起作用。我想通过一些其他方法将字符串转换为小写来测试这种方法。如何将字符串转换为小写?

EDIT ::

其实我找出了问题所在。这有效。当我第一次编写该函数时,我没有ref = tolower(ref)而是tolower(ref),而没有重新分配给ref,所以它没有做任何事情。

bool compare(std::string x, std::string y)
{
    for(auto &ref:x)
        ref = tolower(ref);
    for(auto &ref:y)
        ref = tolower(ref);
    return x < y;
}

EDIT ::

此代码实际上有时会首先使用大写字母进行排序,而其他时候使用大写字母进行排序,因此它无法完全解决问题。

6 个答案:

答案 0 :(得分:2)

执行此操作的常用方法是构建排序规则表。这只是一个表格,给出了每个角色的相对顺序。在您的情况下,您希望每个大写字母紧跟相应的小写字母。

我们可以这样做:

class comp_char { 
    std::vector<int> collation_table;
public:
    comp_char() : collation_table(std::numeric_limits<unsigned char>::max()) {
        std::iota(collation_table.begin(), collation_table.end(), 0);

        for (int i = 0; i < 26; i++) {
            collation_table['a' + i] = i * 2;
            collation_table['A' + i] = i * 2 + 1;
        }
    }

    bool operator()(unsigned char a, unsigned char b) {
        return collation_table[a] < collation_table[b];
    }
};

目前,我忽略了字母与其他字符相对排序的(可能是棘手的)问题。正如它所写的那样,其他一切都在字母之前排序,但是很容易改变,所以(例如)字母排序在其他任何东西之前。尽管如此,它可能并没有产生太大的影响 - 大多数人对'a'&lt; ';'或不。

在任何情况下,一旦构建了可用的排序规则表,您就可以使用它来比较字符串:

struct cmp_str {
    bool operator()(std::string const &a, std::string const &b) {
        comp_char cmp;
        size_t i = 0;
        while (a[i] == b[i] && i < a.size())
            ++i;
        return cmp(a[i], b[i]);
    }
};

...我们可以使用它来进行排序,如下所示:

int main(){
    std::vector<std::string> inputs {
        "This is a test",
        "this is a test",
        "Cats",
        "cats",
        "this thing"
    };

    std::sort(inputs.begin(), inputs.end(), cmp_str());
    std::copy(inputs.begin(), inputs.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"));
}

目前,我只编写了校对表来处理基本的US-ASCII字母。对于实际使用,你通常希望在相应的非重音等效物旁边放置带有重音符号和类似字母的东西。为此,您通常最终会预先构建表,以(部分)匹配Unicode规范之类的内容,以了解应该如何订购。

请注意,此输出与原始问题所需的输出不完全匹配,但我认为在这种情况下问题有误。我无法看到任何方式产生如下订单甚至是合理的:

this is a test
This is a test
this thing

在“t”之前的之后都有“T”排序,这似乎没有意义(或者至少不适合词汇排序,这几乎是人们的意思总是想要字符串)。

答案 1 :(得分:1)

你的解决方案几乎就在那里,如果字符串的小写版本相同,你只需要做一个特例:

std::string to_lower(std::string s)
{
    for (auto & c : s)
        c = std::tolower(c);
    return s;
}

bool string_comp(std::string const & lhs, std::string const & rhs)
{

    auto lhs_lower = to_lower(lhs);
    auto rhs_lower = to_lower(rhs);
    if (lhs_lower == rhs_lower)
        return rhs < lhs;
    return lhs_lower < rhs_lower;
}

这可以使用一些优化。无需复制字符串。当然,您可以进行不区分大小写的比较。但是这个功能在标准库中并不方便,所以我将把这个练习留给你。

答案 2 :(得分:1)

最简单的解决方案是使用标准locale对象提供的排序规则感知排序。

区域设置operator()(std::string, std::string)正是区域设置的排序规则感知比较运算符,因此您只需将其直接插入到std::sort的调用中即可:

// Adjust to the locale you actually want to use
std::sort(strings.begin(), strings.end(), std::locale("en_US.UTF-8"));

ideone

上的示例

答案 3 :(得分:1)

  

为了清楚起见,我的目标是通常的词典类型比较,但如果字符串相同则以某种方式使大写字母遵循小写字母。

这需要两步比较:

  1. 比较不区分大小写模式下的字符串
  2. 如果两个字符串在不区分大小写的模式下相等,我们需要区分大小写的比较结果(将大写字母放在第一位)
  3. 因此,比较器给出:

    class Comparator {
    public:
       bool operator()(std::string const& left, std::string const& right) {
           size_t const size = std::min(left.size(), right.size());
    
           // case-insensitive comparison
           for (size_t i = 0; i != size; ++i) {
               if (std::tolower(left[i]) < std::tolower(right[i])) { return true; }
           }
    
           if (left.size() != right.size()) { return size == left.size(); }
    
           // and now, case-sensitive (reversed)
           return right < left;
       }
    }; // class Comparator
    

答案 4 :(得分:0)

你需要一次比较一个char,停在第一个不同的char然后根据case转换首先返回结果,否则返回原始char:

bool mylt(const std::string& a, const std::string& b) {
    int i=0, na=a.size(), nb=b.size();
    while (i<na && i<nb && a[i]==b[i]) i++;
    if (i==na || i==nb) return i<nb;
    char la=std::tolower(a[i]), lb=std::tolower(b[i]);
    return la<lb || (la==lb && a[i]<b[i]);
}

警告:未经测试的早餐代码

答案 5 :(得分:0)

使用已经具有所需顺序的local,或者按字符比较函数编写字符,然后使用std::lexicographical_compare将其转换为字符串比较函数。

我会先尝试local,但如果事实证明令人沮丧,那么词典就不会太可怕了。

要比较chqracters,请创建两个tuplepair的{​​{1}},lower_case_letter,并在其上调用unchanged_letter。这将首先按小写排序,然后如果失败则保持不变。我忘记了上部和下部的排序顺序:但是如果顺序是倒退的,只需交换哪个小写字母与哪个大写字母配对,你就会颠倒顺序!