【问题标题】:Natural Sorting algorithm自然排序算法
【发布时间】:2010-09-07 06:20:50
【问题描述】:

如何对不同编程语言中的字符串数组naturally 进行排序?在答案中发布您的实现及其使用的语言。

【问题讨论】:

  • 其实有趣的部分是比较函数,它可以用于你喜欢的任何排序算法。
  • 阅读该博客条目的 cmets,似乎自然排序定义不足。

标签: algorithm language-agnostic sorting natural-sort


【解决方案1】:

JavaScript

Array.prototype.alphanumSort = function(caseInsensitive) {
  for (var z = 0, t; t = this[z]; z++) {
    this[z] = [], x = 0, y = -1, n = 0, i, j;

    while (i = (j = t.charAt(x++)).charCodeAt(0)) {
      var m = (i == 46 || (i >=48 && i <= 57));
      if (m !== n) {
        this[z][++y] = "";
        n = m;
      }
      this[z][y] += j;
    }
  }

  this.sort(function(a, b) {
    for (var x = 0, aa, bb; (aa = a[x]) && (bb = b[x]); x++) {
      if (caseInsensitive) {
        aa = aa.toLowerCase();
        bb = bb.toLowerCase();
      }
      if (aa !== bb) {
        var c = Number(aa), d = Number(bb);
        if (c == aa && d == bb) {
          return c - d;
        } else return (aa > bb) ? 1 : -1;
      }
    }
    return a.length - b.length;
  });

  for (var z = 0; z < this.length; z++)
    this[z] = this[z].join("");
}

Source

【讨论】:

  • 这段代码的第三行与它的来源不同。第三行应替换为:this[z] = [];变量 x = 0, y = -1, n = 0, i, j;
【解决方案2】:

对于 MySQL,我个人使用 Drupal 模块中的代码,该模块位于 hhttp://drupalcode.org/project/natsort.git/blob/refs/heads/5.x-1.x:/natsort。安装.mysql

基本上你执行贴出来的SQL脚本来创建函数,然后使用ORDER BY natsort_canon(field_name, 'natural')

这是关于该函数的自述文件: http://drupalcode.org/project/natsort.git/blob/refs/heads/5.x-1.x:/README.txt

【讨论】:

    【解决方案3】:

    如果 OP 询问惯用排序表达式,那么并非所有语言都内置 natural 表达式。对于 c,我会转到 &lt;stdlib.h&gt; 并使用 qsort。有点像:

    /* non-functional mess deleted */
    

    将参数按词法顺序排序。不幸的是,对于那些不使用 c 方式的人来说,这个习语很难解析。


    适当地受到了反对票的惩罚,我实际上阅读了链接的文章。过失。

    在任何情况下,原始代码都不起作用,除了我测试的单一情况。该死。普通的 vanilla c 没有这个功能,也没有在任何常用的库中。

    下面的代码以自然的方式将命令行参数排序为链接。 警告购买者,因为它只是经过轻微测试。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    int naturalstrcmp(const char **s1, const char **s2);
    
    int main(int argc, char **argv){
      /* Sort the command line arguments in place */
      qsort(&argv[1],argc-1,sizeof(char*),
        (int(*)(const void *, const void *))naturalstrcmp);
    
      while(--argc){
        printf("%s\n",(++argv)[0]);
      };
    }
    
    int naturalstrcmp(const char **s1p, const char **s2p){
      if ((NULL == s1p) || (NULL == *s1p)) {
        if ((NULL == s2p) || (NULL == *s2p)) return 0;
        return 1;
      };
      if ((NULL == s2p) || (NULL == *s2p)) return -1;
    
      const char *s1=*s1p;
      const char *s2=*s2p;
    
      do {
        if (isdigit(s1[0]) && isdigit(s2[0])){ 
          /* Compare numbers as numbers */
          int c1 = strspn(s1,"0123456789"); /* Could be more efficient here... */
          int c2 = strspn(s2,"0123456789");
          if (c1 > c2) {
        return 1;
          } else if (c1 < c2) {
        return -1;
          };
          /* the digit strings have equal length, so compare digit by digit */
          while (c1--) {
        if (s1[0] > s2[0]){
          return 1;
        } else if (s1[0] < s2[0]){
          return -1;
        }; 
        s1++;
        s2++;
          };
        } else if (s1[0] > s2[0]){
          return 1;
        } else if (s1[0] < s2[0]){
          return -1;
        }; 
        s1++;
        s2++;
      } while ( (s1!='\0') || (s2!='\0') );
      return 0;
    }
    

    这种方法相当暴力,但它很简单,并且可以在任何命令式语言中复制。

    【讨论】:

      【解决方案4】:

      在 C++ 中,我使用 example code 进行自然排序。代码需要 boost 库。

      【讨论】:

        【解决方案5】:

        我只使用StrCmpLogicalW。它完全符合 Jeff 的要求,因为它与 explorer 使用的 API 相同。不可否认,它不是便携式的。

        在 C++ 中:

        bool NaturalLess(const wstring &lhs, const wstring &rhs)
        {
            return StrCmpLogicalW(lhs.c_str(), rhs.c_str()) < 0;
        }
        
        vector<wstring> strings;
        // ... load the strings
        sort(strings.begin(), strings.end(), &NaturalLess);
        

        【讨论】:

          【解决方案6】:

          这是the article 中的the code 的清理链接到的问题:

          def sorted_nicely(strings): 
              "Sort strings the way humans are said to expect."
              return sorted(strings, key=natural_sort_key)
          
          def natural_sort_key(key):
              import re
              return [int(t) if t.isdigit() else t for t in re.split(r'(\d+)', key)]
          

          但实际上我没有机会这样排序。

          【讨论】:

          • 请注意,我在另一个答案中使用了您的代码:stackoverflow.com/a/22685365/75554。目标软件将在 github 上发布并注明出处。如果这是一个问题,请回复我
          • 没问题。对于您的答案,请注意,在您查看目录和尝试创建文件之间,其他进程可能会创建文件。
          • 您是对的,但不确定如何解决该竞争条件。当您确定是唯一一个在该位置使用该模式写作的人时,这也不是问题
          • 我在那里添加了一个建议,虽然我手头没有详细信息。
          【解决方案7】:

          以下是如何在 Python 中获得类似资源管理器的行为:

          #!/usr/bin/env python
          """
          >>> items = u'a1 a003 b2 a2 a10 1 10 20 2 c100'.split()
          >>> items.sort(explorer_cmp)
          >>> for s in items:
          ...     print s,
          1 2 10 20 a1 a2 a003 a10 b2 c100
          >>> items.sort(key=natural_key, reverse=True)
          >>> for s in items:
          ...     print s,
          c100 b2 a10 a003 a2 a1 20 10 2 1
          """
          import re
          
          def natural_key(astr):
              """See http://www.codinghorror.com/blog/archives/001018.html"""
              return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', astr)]
          
          def natural_cmp(a, b):
              return cmp(natural_key(a), natural_key(b))
          
          try: # use explorer's comparison function if available
              import ctypes
              explorer_cmp = ctypes.windll.shlwapi.StrCmpLogicalW
          except (ImportError, AttributeError):
              # not on Windows or old python version
              explorer_cmp = natural_cmp        
          
          if __name__ == '__main__':
              import doctest; doctest.testmod()
          

          为了支持 Unicode 字符串,应使用.isdecimal() 而不是.isdigit()

          .isdigit() 也可能在某些语言环境(例如'\xb2' ('²') in cp1252 locale on Windows)中对 Python 2 上的字节字符串失败(返回值未被int() 接受)。

          【讨论】:

          • 如果人们发现这篇 2008 年的帖子而不是 2 个较新的 Python 帖子,我想补充一点:'int' 适用于许多有用的案例(例如 image3.jpg 和 image10.jpg)但在 ['elm1', 'Elm2'] 和 ['0.501', '0.55'] 和 [0.01, 0.1, 1] 等情况下失败...请参阅stackoverflow.com/questions/4836710/… for lower() 和我更通用的 Python 解决方案自然排序顺序。
          • @ScottLawton:将您的解决方案与StrCmpLogicalW 函数进行比较。阅读问题中链接的博文。
          【解决方案8】:

          只是 Eric Normand 在 Common Lisp 中的一些出色作品的链接:

          http://www.lispcast.com/wordpress/2007/12/human-order-sorting/

          【讨论】:

            【解决方案9】:

            对于 Tcl,lsort 的 -dict(字典)选项:

            % lsort -dict {a b 1 c 2 d 13}
            1 2 13 a b c d
            

            【讨论】:

              【解决方案10】:

              请注意,对于大多数此类问题,您可以咨询Rosetta Code Wiki。我从排序整数的条目中调整了我的答案。

              在系统的编程语言中做这样的事情通常比使用专门的字符串处理语言更难看。对 Ada 来说幸运的是,最新版本有一个库例程可以完成此类任务。

              对于 Ada 2005,我相信您可以执行以下操作(警告,未编译!):

              type String_Array is array(Natural range <>) of Ada.Strings.Unbounded.Unbounded_String;
              function "<" (L, R : Ada.Strings.Unbounded.Unbounded_String) return boolean is
              begin
                 --// Natural ordering predicate here. Sorry to cheat in this part, but
                 --// I don't exactly grok the requirement for "natural" ordering. Fill in 
                 --// your proper code here.
              end "<";
              procedure Sort is new Ada.Containers.Generic_Array_Sort 
                (Index_Type   => Natural;
                 Element_Type => Ada.Strings.Unbounded.Unbounded_String,
                 Array_Type   => String_Array
                );
              

              使用示例:

                  using Ada.Strings.Unbounded;
              
                  Example : String_Array := (To_Unbounded_String ("Joe"),
                                             To_Unbounded_String ("Jim"),
                                             To_Unbounded_String ("Jane"),
                                             To_Unbounded_String ("Fred"),
                                             To_Unbounded_String ("Bertha"),
                                             To_Unbounded_String ("Joesphus"),
                                             To_Unbounded_String ("Jonesey"));
              begin
                  Sort (Example);
                  ...
              end;
              

              【讨论】:

                【解决方案11】:

                在 C 中,此解决方案正确处理带有前导零的数字:

                #include <stdlib.h>
                #include <ctype.h>
                
                /* like strcmp but compare sequences of digits numerically */
                int strcmpbynum(const char *s1, const char *s2) {
                  for (;;) {
                    if (*s2 == '\0')
                      return *s1 != '\0';
                    else if (*s1 == '\0')
                      return 1;
                    else if (!(isdigit(*s1) && isdigit(*s2))) {
                      if (*s1 != *s2)
                        return (int)*s1 - (int)*s2;
                      else
                        (++s1, ++s2);
                    } else {
                      char *lim1, *lim2;
                      unsigned long n1 = strtoul(s1, &lim1, 10);
                      unsigned long n2 = strtoul(s2, &lim2, 10);
                      if (n1 > n2)
                        return 1;
                      else if (n1 < n2)
                        return -1;
                      s1 = lim1;
                      s2 = lim2;
                    }
                  }
                }
                

                如果你想和qsort一起使用,使用这个辅助函数:

                static int compare(const void *p1, const void *p2) {
                  const char * const *ps1 = p1;
                  const char * const *ps2 = p2;
                  return strcmpbynum(*ps1, *ps2);
                }
                

                你可以按照以下顺序做某事

                char *lines = ...;
                qsort(lines, next, sizeof(lines[0]), compare);
                

                【讨论】:

                  【解决方案12】:

                  Python,使用 itertools:

                  def natural_key(s):
                      return tuple(
                          int(''.join(chars)) if isdigit else ''.join(chars)
                          for isdigit, chars in itertools.groupby(s, str.isdigit)
                      )
                  

                  结果:

                  >>> natural_key('abc-123foo456.xyz')
                  ('abc-', 123, 'foo', 456, '.xyz')
                  

                  排序:

                  >>> sorted(['1.1.1', '1.10.4', '1.5.0', '42.1.0', '9', 'banana'], key=natural_key)
                  ['1.1.1', '1.5.0', '1.10.4', '9', '42.1.0', 'banana']
                  

                  【讨论】:

                    【解决方案13】:

                    我在 Clojure 1.1 上的实现:

                    (ns alphanumeric-sort
                      (:import [java.util.regex Pattern]))
                    
                    (defn comp-alpha-numerical
                      "Compare two strings alphanumerically."
                      [a b]
                      (let [regex (Pattern/compile "[\\d]+|[a-zA-Z]+")
                            sa (re-seq regex a)
                            sb (re-seq regex b)]
                        (loop [seqa sa seqb sb]
                          (let [counta (count seqa)
                                countb (count seqb)]
                            (if-not (not-any? zero? [counta countb]) (- counta countb)
                              (let [c (first seqa)
                                    d (first seqb)
                                    c1 (read-string c)
                                    d1 (read-string d)]
                                 (if (every? integer? [c1 d1]) 
                                   (def result (compare c1 d1)) (def result (compare c d)))
                                 (if-not (= 0 result) result (recur (rest seqa) (rest seqb)))))))))
                    
                    (sort comp-alpha-numerical ["a1" "a003" "b2" "a10" "a2" "1" "10" "20" "2" "c100"])
                    

                    结果:

                    (“1”“2”“10”“20”“a1”“a2”“a003”“a10”“b2”“c100”)

                    【讨论】:

                      【解决方案14】:

                      php 有一个简单的函数“natsort”可以做到这一点,我自己实现了它:

                      <?php
                      $temp_files = array('+====','-==',"temp15-txt","temp10.txt",
                      "temp1.txt","tempe22.txt","temp2.txt");
                      $my_arr = $temp_files;
                      
                      
                      natsort($temp_files);
                      echo "Natural order: ";
                      print_r($temp_files);
                      
                      
                      echo "My Natural order: ";
                      usort($my_arr,'my_nat_func');
                      print_r($my_arr);
                      
                      
                      function is_alpha($a){
                          return $a>='0'&&$a<='9' ;
                      }
                      
                      function my_nat_func($a,$b){
                          if(preg_match('/[0-9]/',$a)){
                              if(preg_match('/[0-9]/',$b)){
                                  $i=0;
                                  while(!is_alpha($a[$i]))    ++$i;
                                  $m  = intval(substr($a,$i));            
                                  $i=0;
                                  while(!is_alpha($b[$i]))    ++$i;
                                  $n  = intval(substr($b,$i));
                                  return $m>$n?1:($m==$n?0:-1);
                              }
                              return 1;
                          }else{
                              if(preg_match('/[0-9]/',$b)){
                                  return -1;
                              }
                              return $a>$b?1:($a==$b?0:-1);
                          }
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 2010-10-24
                        • 2010-10-13
                        • 1970-01-01
                        • 2012-07-26
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-01-09
                        相关资源
                        最近更新 更多