さまざまなプログラミング言語で文字列の配列を自然に並べ替えるにはどうすればよいですか? 実装とそれがどの言語であるかを回答に投稿してください。
14 に答える
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()
は、一部のロケール(Windows の cp1252 ロケールの '\xb2' ('²') など) でint()
、Python 2 のバイト文字列に対して失敗する ( で受け入れられない値を返す) 場合もあります。
MySQL の場合、私は個人的に、hhttp://drupalcode.org/project/natsort.git/blob/refs/heads/5.x-1.x:/natsort.install.mysql で入手できる Drupal モジュールのコードを使用します。
基本的には、投稿したSQLスクリプトを実行して関数を作成し、ORDER BY natsort_canon(field_name, 'natural')
関数に関する readme は次のとおりです: http://drupalcode.org/project/natsort.git/blob/refs/heads/5.x-1.x:/README.txt
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("");
}
質問がリンクされている記事のコードのクリーンアップを次に示します。
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)]
しかし、実際には、この方法で何かを並べ替える機会はありませんでした。
OPが偶像的なソート式について尋ねている場合、すべての言語に自然な式が組み込まれているわけではありませ<stdlib.h>
んqsort
。次の行の何か:
/* non-functional mess deleted */
引数を字句順にソートします。残念ながら、このイディオムは、c の方法を使用していない人にとっては解析がかなり困難です。
反対票に適切に懲らしめられて、リンクされた記事を実際に読みました。Mea culpa。
いずれにせよ、私がテストした 1 つのケースを除いて、元のコードは機能しませんでした。くそ。普通のバニラ c にはこの関数はなく、通常のライブラリにもありません。
以下のコードは、コマンド ライン引数をリンクされているとおりに自然な方法で並べ替えます。emptorは軽くテストされているだけなので注意してください。
#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;
}
このアプローチはかなり強引ですが、単純であり、おそらくどの命令型言語でも複製できます。
C ++では、このサンプルコードを使用して自然ソートを実行します。コードにはブーストライブラリが必要です。
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);
Eric Normand による Common Lisp の素晴らしい作品へのリンク:
http://www.lispcast.com/wordpress/2007/12/human-order-sorting/
私はStrCmpLogicalWを使用しています。これは、エクスプローラーが使用するのと同じ API であるため、Jeff が望んでいることを正確に実行します。確かに、それはポータブルではありません。
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);
このような質問のほとんどについては、 RosettaCodeWikiを参照するだけでよいことに注意してください。整数をソートするために、エントリからの回答を適合させました。
システムのプログラミング言語では、このようなことを行うと、一般に、特殊な文字列処理言語よりも醜くなります。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;
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']
Tclの場合、lsortの-dict(辞書)オプション:
% lsort -dict {a b 1 c 2 d 13}
1 2 13 a b c d
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);
}
}