【问题标题】:Sorting std::strings with numbers in them?对带有数字的 std::strings 进行排序?
【发布时间】:2011-06-05 02:00:39
【问题描述】:

我目前正在按 std::string

30

谢谢

【问题讨论】:

  • 当然,这取决于用例,但对于文件名,我实际上更喜欢 XP 之前的方式。我讨厌软件试图变得过于聪明,因为那样它就会开始把事情搞砸......尝试在 >=XP 中对十六进制文件名列表进行排序。

标签: c++ string sorting


【解决方案1】:

您可以创建自定义比较函数以与std::sort 一起使用。该函数必须检查字符串是否以数值开头。如果是这样,请使用字符串流等机制将每个字符串的数字部分转换为int。然后比较两个整数值。如果值比较相等,则按字典顺序比较字符串的非数字部分。否则,如果字符串不包含数字部分,只需像往常一样按字典顺序比较两个字符串。

基本上,类似于以下(未经测试的)比较函数:

bool is_not_digit(char c)
{
    return !std::isdigit(c);
}

bool numeric_string_compare(const std::string& s1, const std::string& s2)
{
    // handle empty strings...

    std::string::const_iterator it1 = s1.begin(), it2 = s2.begin();

    if (std::isdigit(s1[0]) && std::isdigit(s2[0])) {
        int n1, n2;
        std::stringstream ss(s1);
        ss >> n1;
        ss.clear();
        ss.str(s2);
        ss >> n2;

        if (n1 != n2) return n1 < n2;

        it1 = std::find_if(s1.begin(), s1.end(), is_not_digit);
        it2 = std::find_if(s2.begin(), s2.end(), is_not_digit);
    }

    return std::lexicographical_compare(it1, s1.end(), it2, s2.end());
}

然后……

std::sort(string_array.begin(), string_array.end(), numeric_string_compare);

编辑:当然,此算法仅在您对数字部分出现在字符串开头的字符串进行排序时才有用。如果您正在处理数字部分可以出现在字符串中任何地方的字符串,那么您需要一个更复杂的算法。请参阅http://www.davekoelle.com/alphanum.html 了解更多信息。

【讨论】:

  • +1 因为您的代码处理其中一个在开头包含数字但另一个不包含数字的情况。
  • atoi 非常适合此目的,您甚至不需要为不以数字开头的字符串编写特殊代码(尽管它们会从头开始排序)。或者strtod,如果你想控制它。
  • @Ben,是的,atoi 会比 std::stringstream 更有效率。
  • FWIW,我写了一个类似的函数,它使用strtoll 来表示stackoverflow question here,但它在另一种方式上不太正确 - 假设在不比较数字时直接比较字符是所需的顺序。
  • 转换为 int(例如,通过 atoi)假设数字的字符串表示可以成功转换为 int。长串数字,转换会溢出...
【解决方案2】:

如果您的目标是 Windows (XP+) 并且有能力将字符串转换为 utf-16,则可以使用 Shlwapi 的 StrCmpLogicalW 函数。详情请见msdn

否则,ICU 在其整理器中提供此功能。见UCOL_NUMERIC_COLLATION

【讨论】:

    【解决方案3】:

    这是一个不转换为整数的版本,因此适用于长数字字符串,而与 sizeof(int) 无关。

    #include <cctype>
    #include <cstddef>
    #include <cstring>
    #include <string>
    
    int numcmp(const char *a, const char *aend, const char *b, const char *bend)
    {
      for (;;) {
        if (a == aend) {
          if (b == bend)
            return 0;
          return -1;
        }
        if (b == bend)
          return 1;
        if (*a == *b) {
          ++a, ++b;
          continue;
        }
        if (!isdigit((unsigned char) *a) || !isdigit((unsigned char) *b))
          return *a - *b;
    
        // skip leading zeros in both strings
        while (*a == '0' && ++a != aend)
          ;
        while (*b == '0' && ++b != aend)
          ;
    
        // skip to end of the consecutive digits
        const char *aa = a;
        while (a != aend && isdigit((unsigned char) *a))
          ++a;
        std::ptrdiff_t alen = a - aa;
    
        const char *bb = b;
        while (b != bend && isdigit((unsigned char) *b))
          ++b;
        std::ptrdiff_t blen = b - bb;
    
        if (alen != blen)
          return alen - blen;
    
        // same number of consecutive digits in both strings
        while (aa != a) {
          if (*aa != *bb)
            return *aa - *bb;
          ++aa, ++bb;
        }
      }
    }
    
    int numcmp(const std::string& a, const std::string& b)
    {
      return numcmp(a.data(), a.data() + a.size(),
                    b.data(), b.data() + b.size());
    }
    
    int numcmp(const char *a, const char *b)
    {
      return numcmp(a, a + strlen(a), b, b + strlen(b));
    }
    

    【讨论】:

    • numcmp("0001", "001") == 0,这意味着当您将numcmp用作集合中的键排序函数时,集合中只会存储这些字符串中的一个。这可能不是故意的。
    • 是的,确实如此。对于这个问题的所有解决方案,如果字符串不同但 numcmp 表示它们相等,我们需要确定我们想要发生的情况
    【解决方案4】:

    这对我有用(假设没有前导零),即这个想法是语音比较可以仅应用于具有相同位数的数字。

        auto numeric_str_cmp = [](const std::string& a, const std::string& b) -> bool {
                                    return (a.size() < b.size()) || (a.size() == b.size() && a < b);
                                 };
        std::sort(numeric_strings.begin(), numeric_strings.end(), numeric_str_cmp);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-18
      • 2021-07-23
      • 1970-01-01
      • 2017-09-07
      • 2013-08-02
      • 1970-01-01
      • 1970-01-01
      • 2020-08-23
      相关资源
      最近更新 更多