10

我想以人类对它们进行排序的方式对字母数字字符串进行排序。即,“A2”在“A10”之前,“a”当然在“Z”之前!有没有办法不用写一个迷你解析器?理想情况下,它还将“A1B1”放在“A1B10”之前。我看到问题“Microsoft SQL 2005 中的自然(人类字母数字)排序”有一个可能的答案,但它使用各种库函数,“使用 IComparer 为人类排序字符串”也是如此。

以下是当前失败的测试用例:

#include <set>
#include <iterator>
#include <iostream>
#include <vector>
#include <cassert>

template <typename T>
struct LexicographicSort {
  inline bool operator() (const T& lhs, const T& rhs) const{
    std::ostringstream s1,s2;
    s1 << toLower(lhs); s2 << toLower(rhs);
    bool less = s1.str() < s2.str();
    //Answer: bool less = doj::alphanum_less<std::string>()(s1.str(), s2.str());
    std::cout<<s1.str()<<" "<<s2.str()<<" "<<less<<"\n";
    return less;
  }

  inline std::string toLower(const std::string& str) const {
    std::string newString("");
    for (std::string::const_iterator charIt = str.begin();
         charIt!=str.end();++charIt) {
          newString.push_back(std::tolower(*charIt));
        }
        return newString;
      }
};


int main(void) {
  const std::string reference[5] = {"ab","B","c1","c2","c10"};
  std::vector<std::string> referenceStrings(&(reference[0]), &(reference[5]));

  //Insert in reverse order so we know they get sorted
  std::set<std::string,LexicographicSort<std::string> > strings(referenceStrings.rbegin(), referenceStrings.rend());

  std::cout<<"Items:\n";
  std::copy(strings.begin(), strings.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
  std::vector<std::string> sortedStrings(strings.begin(), strings.end());
  assert(sortedStrings == referenceStrings);
}
4

4 回答 4

5

有没有办法不用写一个迷你解析器?

让别人这样做?

我正在使用这个实现:http ://www.davekoelle.com/alphanum.html ,我也修改了它以支持 wchar_t。

于 2010-05-06T20:19:50.097 回答
2

这真的取决于你所说的“解析器”是什么意思。如果您想避免编写解析器,我认为您应该利用库函数。

  • 将字符串视为统一为字母、数字或“其他”的子序列序列。
  • isalnum使用回溯检查+-是否为数字来获取每个字符串的下一个字母数字序列。使用strtold就地查找数字子序列的结尾。
  • 如果一个是数字,一个是字母,则带有数字子序列的字符串在前。
  • 如果一个字符串的字符用完了,它会先出现。
  • 用于strcoll比较当前语言环境中的字母子序列。
  • 用于strtold比较当前语言环境中的数字子序列。
  • 重复直到完成一个或两个字符串。
  • 与 断绝关系strcmp

该算法在比较精度超过long double.

于 2010-05-06T20:30:49.323 回答
0

如果没有任何解析,就无法将人类书写的数字(高值首先去除前导零)和普通字符作为同一字符串的一部分进行比较。

不过,解析不需要非常复杂。一个简单的哈希表,用于处理区分大小写和去除特殊字符('A'='a'=1,'B'='b'='2,... or 'A'=1,'a' =2,'B'=3,..., '-'=0(strip)),将字符串重新映射到散列值数组,然后截断数字情况(如果遇到数字并且最后一个字符是数,将最后一个数乘以 10 并加上当前值)。

从那里,正常排序。

于 2010-05-06T20:15:07.950 回答
0

有没有办法在不编写迷你解析器的情况下做到这一点?我想答案是否定的。但是编写解析器并不难。不久前我不得不这样做来对我们公司的股票编号进行排序。基本上只需扫描数字并将其转换为数组。检查每个字符的“类型”:字母、数字,也许您还有其他需要处理的特殊字符。就像我必须特别对待连字符一样,因为我们希望 ABC 在 AB-A 之前排序。然后开始剥离字符。只要它们与第一个字符的类型相同,它们就会进入同一个桶。一旦类型发生变化,您就开始将它们放入不同的存储桶中。然后,您还需要一个逐桶比较的比较函数。当两个桶都是 alpha 时,您只需进行正常的 alpha 比较。当两者都是数字时,将两者都转换为整数并进行整数比较,或将较短的长度填充到较长的长度或等效的长度。当它们是不同的类型时,你需要一个规则来比较它们,比如 AA 是在 A-1 之前还是之后?

这不是一项微不足道的工作,您必须为可能出现的所有奇怪情况制定规则,但我认为您可以在几个小时内完成。

于 2010-05-06T19:51:45.537 回答