【问题标题】:C++ Printing a map in a case-insensitive, alphabetical orderC++ 以不区分大小写的字母顺序打印地图
【发布时间】:2014-05-19 02:57:55
【问题描述】:

我有一个:

map<string, map<int,int>>

有没有办法按字母顺序打印此地图的内容,但不区分大小写?例如,让它按以下顺序打印:

A : 1:1, 2:2
a : 3:1
an : 2:1
And : 4:1
and : 3:1

目前,我正在使用以下方式进行打印:

for (auto it = tokens.begin(); it != tokens.end(); ++it){
    cout << it->first << " : ";
    auto const &internal_map = it->second;
    for (auto it2 = internal_map.begin(); it2 != internal_map.end(); ++it2){
        if (it2 != internal_map.begin())
            cout << " , ";
        cout << it2->first << ":" << it2->second;
    }       
    cout << endl;
}

这会打印所有内容,但是,它首先是全部大写,然后是全部小写。例如:

A : 1:1, 2:2
And : 4:1
a : 3:1
an : 2:1
and : 3:1

【问题讨论】:

    标签: c++ sorting c++11 map alphabetical


    【解决方案1】:

    如已接受的答案中所述,您希望将map 与自定义比较功能一起使用。诀窍是进行适当的比较。您不希望完全不区分大小写的比较或“与”和“与”将相等,只允许其中一个出现在地图中。您的样本数据并未涵盖所有案例;例如,“An”、“And”、“AN”、“AND”的顺序是什么?以下比较函数将它们排序为“AN”、“An”、“AND”、“And” - 较短的字符串总是小于相同字符的较长字符串,并且第一个具有不同大小写的字符是与大写的决胜局- 小写前的大小写。

    struct CaseAwareCompare
    {
        bool operator()(const char * left, const char * right) const
        {
            bool tied = true, tiebreaker = false;
            for (int i = 0; left[i] != 0; ++i)
            {
                if (right[i] == 0)
                    return false;
                if (tolower(left[i]) != tolower(right[i]))
                    return tolower(left[i]) < tolower(right[i]);
                if (tied && left[i] != right[i])
                {
                    tied = false;
                    tiebreaker = left[i] < right[i];
                }
            }
            return (right[i] != 0) || (!tied && tiebreaker);
        }
    
        bool operator()(const string & left, const string & right) const
        {
            return operator()(left.c_str(), right.c_str());
        }
    };
    

    我不知道该怎么称呼它;这不是不区分大小写的比较,因为它区分不同大小写的输入。我最终决定将其称为区分大小写的比较。

    【讨论】:

    • +1 我很久没看这个问题了,我忘了它必须支持'A'和'a'等。看起来确实很可靠。
    【解决方案2】:

    如果您想要一个自定义的“区分大小写”比较器,如 proposed by Mark Ransom,它遵循您的语言的字母顺序(甚至您自己的字母顺序实现)和 Unicode,您可以使用 std::collat​​e 或 boost::整理者。

    struct CaseAwareCompare
    {        
        operator()(const std::string& strLeft, const std::string& strRight) const
        {
            using namespace boost::locale;
    
            std::locale loc;
    
            // Quaternary level : consider all case, accents, and punctuation.
            // The words must be identical in terms of Unicode representation.
            if (-1 == use_facet<collator<char> >(loc).compare(collator_base::quaternary,                                                               strLeft.begin(),
                                                              strLeft.end(),
                                                              strRight.begin(),
                                                              strRight.end())) {
        
                return true;
            }
    
            return false;
        }    
    };
    

    注意:您可能需要使用 collat​​or_base::identical 级别或使用规范化的 Unicode 字符串,具体取决于您的应用程序。
    例如,具有两个不同的字符串(不匹配的代码点)但具有相同的 Unicode 表示(向用户显示)。
    "señor" : 带ñ。
    "señor" : 用 n + ◌̃。

    • 无归一化有collat​​or_base::quaternary,两者 字符串将在同一个地图元素中。

    • 没有规范化有 collat​​or_base::identical,两者 字符串将位于不同的地图元素中,但如果您显示 将元素映射到用户,他不明白为什么会有两次 相同的字符串。

    • 在您的应用程序中对所有字符串进行规范化,这两个字符串 不会再不同了,你可以 使用 collat​​or_base::quaternary 无后顾之忧。

       

    【讨论】:

      【解决方案3】:

      有没有办法按字母顺序打印此地图的内容,但不区分大小写?

      是的。

      1. 您必须创建一个自定义的比较函子,以不区分大小写的方式比较两个字符串。

        struct cicompare
        {
           bool operator()(std::string const& lhsIn, std::string const& rhsIn) const
           {
             char const* lhs = lhsIn.c_str();
             char const* rhs = rhsIn.c_str();
             for ( ; *lhs != '\0' && *rhs != '\0'; ++lhs, ++rhs )
             {
                if ( tolower(*lhs) != tolower(*rhs) )
                {
                   return ( tolower(*lhs) < tolower(*rhs) );
                }
                else if ( *lhs != *rhs)
                {
                   if ( *(lhs+1) == '\0' && *(rhs+1) == '\0' )
                   {
                      return (*lhs < *rhs);
                   }
                }
             }
             return (tolower(*lhs) < tolower(*rhs));
           }
        };
        
      2. 使用不区分大小写的比较函子来创建映射。

        map<string, map<int,int>, cicompare> mymap;
        
      3. 如果您不想以不区分大小写的方式存储排序的地图,请在打印前使用cicompare 创建原始地图的副本并打印新地图。

        map<string, map<int,int>, cicompare> mapForPrinting;
        mapForPrinting.insert(originalMap.start(), originalMap.end());
        

      【讨论】:

      • 这可能很好,但根本不同在于它改变了容器的存储顺序,而不是打印时的一次性迭代排序。但是,您的比较将在相同字符串的末尾运行,并且永远无法到达第二个 return 语句。
      • @TonyD 感谢您找出逻辑错误。现在已经修好了。
      • @MarkRansom 同上我对 Tony D 说的话。
      • @TonyD 我发布了带有比较功能的第二个答案,对此我进行了很多思考 - 看看你的想法。
      • @CGutz 映射基于树结构,因此它不会与每个键进行比较,仅与它们的 log(n) 进行比较。而且我认为这个比较功能有一些问题,例如“AxB”和“AXB”被认为是相同的。
      【解决方案4】:

      我认为通常的方法是为要显示的元素创建一个迭代器索引:

      // Return a range of iterators to the elements
      // of the given range.
      template <typename Range>
      auto make_index(Range& range)
        -> vector<decltype(range.begin())> {
        vector<decltype(range.begin())> index;
        for (auto i = begin(range), e = end(range); i != e; ++i) {
          index.push_back(i);
        }
        return index;
      }
      

      并根据显示标准对迭代器进行排序:

      auto index = make_index(tokens);
      using iterator = decltype(tokens.begin());
      sort(begin(index), end(index), [](iterator a, iterator b){
        return boost::algorithm::ilexicographical_compare(a->first, b->first);
      });
      

      记得取消引用迭代器以进行显示:

      cout << "Sorted:\n";
      for (auto&& i : index) {
        cout << i->first << ':';
        for (auto const& j : i->second) {
          cout << ' ' << j.first << ':' << j.second;
        }
        cout << '\n';
      }
      

      (See it all live at Coliru).

      我使用了boost::algorithm::ilexicographical_compare,Boost 的不区分大小写的依赖于区域设置的字符串比较,以节省一些输入。

      【讨论】:

        【解决方案5】:

        您可以将数据存储在向量中。您可以将数据作为structurepair 存储在向量中。如果你使用pair 那么,

        vector< pair< string, map<int,int> > > needToSort;
        

        那你就可以在里面调用std::sort了。

        sort(needToSort.begin(), needToSort.end(), compareFunction);
        

        compareFunction 是,

        bool compareFunction( pair< string, map<int,int> > firstElement, pair< string, map<int,int> > secondElement)
        {
            string firstString = firstElement.first;
            string secondString = secondElement.first;
            for(int i=0;i<firstString.size();i++)
                firstString[i] = toLower(firstString [i]);
        
            for(int i=0;i<secondString.size();i++)
                secondString [i] = toLower(secondString[i]);
        
           return firstString < secondString;
        }
        

        然后您可以打印应该给您所需结果的向量。

        【讨论】:

          猜你喜欢
          • 2011-08-14
          • 2020-03-20
          • 2023-03-27
          • 1970-01-01
          • 2020-12-24
          • 2011-08-22
          • 1970-01-01
          • 2012-09-20
          • 1970-01-01
          相关资源
          最近更新 更多