wind0wsのような感じのファイル名ソートをしたい

普通の文字列比較では、"a999.txt"と"a1000.txt"では、後者が小さいとみなされます。
しかし、後者が大きいと判定したほうが、自然です。
Wind0wsの場合は数値の部分は数値の比較を行ってくれるようで、ファイル名ソートすると、良い感じにやってくれます。
今回はそんな感じにソートします。


単純に考えれば、数字の部分は数値比較、文字列の部分は文字列比較すればいいです。多分。
もっとうまい方法がありそうですが、僕は頭が悪いので思いつきませんでした。

#!/usr/bin/perl

use strict;
use warnings;
use utf8;

# カレントディレクトリのファイル名のリストを取得
my @filenames = (<*>);

# ソート
@filenames = sort { compare($a,$b) } @filenames; #
#@filenames = sort @filenames; # 通常ソート

# 出力
foreach my $filename(@filenames){
	print "$filename\n";
}



# 文字列を数値以外の文字列と数値に分割
# abc123def456 => abc|123|def|456
sub split_strnum{
	my $str = shift;
	my @data = ('');
	my $i=0;
	my $flag = 0;
	
	# 1文字取得
	while($str =~/(.)/g){
		my $ch = $1;
		if($ch =~ /\d/){
			# 数字
			if($flag == 1){
				$i++;
				push @data, '';
			}
				$flag = 2;
			$data[$i] .= $ch;
		}else{
			# その他
			if($flag == 2){
				$i++;
				push @data, '';
			}
				$flag = 1;
			$data[$i] .= $ch;
		}
	}
	return \@data;
}

# それっぽい比較関数
sub compare{
	my $data1 = split_strnum shift;
	my $data2 = split_strnum shift;
	
	# 二つの配列から最大長取得
	my $length = @$data1 < @$data2 ? @$data2 : @$data1;
	
	for(my $i=0; $i<$length; ++$i){
		my $s1 = $data1->[$i] ? $data1->[$i] : "";
		my $s2 = $data2->[$i] ? $data2->[$i] : "";
		my $result;
		
		if($s1 =~ /^\d+$/ && $s2 =~ /^\d+$/){
			# 両方数字
			$result = $s1 <=> $s2;
		}else{
			# 数字じゃなかったら文字列で比較
			$result = $s1 cmp $s2;
		}
		
		# 等しくないなら比較結果を返す
		if($result != 0){
			return $result;
		}
	}
	
	# 完全に一致
	return 0;
}