C ++以不区分大小写的字母顺序打印地图

时间:2014-04-07 02:36:33

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

我有一个:

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

5 个答案:

答案 0 :(得分: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());
    

答案 1 :(得分:3)

如接受的答案中所述,您希望使用带有自定义比较功能的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;
        int i;
        for (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());
    }
};

我为什么称之为挣扎;它不是一个不区分大小写的比较,因为它区分不同情况下的输入。我最终认为它应该被称为案例感知比较。

答案 2 :(得分:0)

您可以将数据存储在矢量中。您可以将数据存储为向量中的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;
}

然后你可以打印出能给你所需结果的矢量。

答案 3 :(得分:0)

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

// 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的不区分大小写的区域设置字符串比较,以节省一些输入。

答案 4 :(得分:0)

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

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 ::相同的级别或使用规范化的Unicode字符串。
例如,使用两个不同的字符串(不匹配的代码点),但是使用相同的Unicode表示形式(显示给用户)。
“señor”:带有ñ。
“señor”:带有n + ◌̃。

  • 没有规范化使用collat​​or_base :: quaternary ,这两个 字符串将在同一地图元素中。

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

  • 在您的应用程序中所有字符串标准化后,两个字符串 不会再有不同了,你可以 不用担心使用collat​​or_base :: quaternary。