「自然順ソート」あれこれ
2016/09/20Finderでのソート順って?
まず、Finderでのソート順って何かっていいますと、 ファイル1,ファイル2,ファイル3…ファイル10,ファイル11,ファイル12 となるようなソート方法のことです。文字列の後に数字が来るような場合でも数値順にソートさせたい。 perlなどで普通に文字列順にソートさせても数値でソートさせても ファイル1,ファイル10,ファイル11,ファイル12,ファイル2,ファイル3… となってしまい、思ったような処理ができません。 Finderでのソート順については、降りてきた天の声によると@JunTajima FinderはJIS X 4061 照合順番を実装していると木田さんが言ってたような。
Xojo内でCocoaのライブラリを呼び出してソート
フロントエンドのツールはXojoなので、Xojo内でできればなと思って、過去にQiitaにされていたアップされていたこちらの神エントリをもとにどうにかソートできるようにしたのが以下。さすがです@monokanoさん。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
'ファンクション宣言 Declare function localizedStandardCompare lib "Cocoa" selector "localizedStandardCompare:" (s1 as CFStringRef, s2 as CFStringRef) as Integer '最終出力用変数の定義 Dim mySortedArray() AS String '自然順ソート実行 For Each myItem As String In myArray If mySortedArray.Ubound = -1 then mySortedArray.Append (myItem) Else For myCount AS Integer = mySortedArray.Ubound Downto 0 Dim myMatchItem As String = mySortedArray(myCount) Dim myCompareResult AS Integer myCompareResult = localizedStandardCompare( myMatchItem, myItem ) If myCompareResult = - 1 then mySortedArray.Insert(myCount+1, myItem) Exit ElseIf myCount = 0 then mySortedArray.Insert(myCount, myItem) End If Next myCount End If Next Return mySortedArray() |
Xojo内でモジュール化しておいて元の配列を投げ、ソート後の配列を得る、みたいな使い方です。今回の目的としてはFinderのソート順の再現なので、Finderと同じエンジンを使うこちらが一番目的に叶いそうです。ただもちろんCocoaのライブラリを呼び出している以上Mac専用にはなります。
Applescriptでの自然順ソート
AppleScriptでもできそうかなということでこちらの投稿をもとに作ってみたのが以下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
on run (myOriginalJoinedString) --入力値を分割してリストに set mySplitArray to splitList(item 1 of myOriginalJoinedString, ",") --ソート実行 considering numeric strings set mySortedList to simple_sort(mySplitArray) end considering --リストを結合して文字列に set mySortedJoinString to joinList(mySortedList, ",") --結果を返す return mySortedJoinString end run --自然順ソートアルゴリズム on simple_sort(my_list) set the index_list to {} set the sorted_list to {} repeat (the number of items in my_list) times set the low_item to "" repeat with i from 1 to (number of items in my_list) if i is not in the index_list then set this_item to item i of my_list as text if the low_item is "" then set the low_item to this_item set the low_item_index to i else if this_item comes before the low_item then set the low_item to this_item set the low_item_index to i end if end if end repeat set the end of sorted_list to the low_item set the end of the index_list to the low_item_index end repeat return the sorted_list end simple_sort --リスト分割 on splitList(theText, aDelimiter) set tmp to AppleScript's text item delimiters set AppleScript's text item delimiters to aDelimiter set theList to get text items of theText set AppleScript's text item delimiters to tmp return theList end splitList --リスト結合 on joinList(theList, aDelimiter) set tmp to AppleScript's text item delimiters set AppleScript's text item delimiters to aDelimiter set theText to theList as string set AppleScript's text item delimiters to tmp return theText end joinList |
他の処理系に投げるので一回文字列に結合して引数として投げ、ソートした後に再結合した文字列を得ます。でそれをまた分解して配列に。
AppleScriptはテキストアイテムデリミタでの文字列の分解結合処理がいつもながらややこしいのですが、そのあたりはまあご愛敬ということで。こちらもAppleScriptを使う以上Mac専用です。まあそのうちAppleScriptで自然順ソートさせなきゃならないケースも出てくるかも知れないし。
PHPのnatsort関数でソート
PHPには自然順ソートができるnatsortという関数がデフォルトで入っているとのことなのでやってみたのが以下。
1 2 3 4 5 6 7 |
<?php $joinedOriginalString = $argv[1]; $myArray = explode(",", $joinedOriginalString); natsort($myArray); $joinedString = implode(",", $myArray); print($joinedString); ?> |
一番簡単かも。Mac環境に依存しないのでそういう意味での使い出もありそうです。
3種類の自然順ソートを収録したXojoのファイルをnatsort_sample.zip
1 2 3 4 5 |
#! /usr/bin/perl my $imagePath = "/Users/JunTajima/Desktop/testfolder"; my $appleScriptPath = "/Users/JunTajima/Desktop/testapplescript.scpt"; my $result = `osascript $appleScriptPath $imagePath`; print $result; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
on run argv set myImageFolderPathPOSIX to item 1 of argv tell application "Finder" set myImageFolderPath to myImageFolderPathPOSIX as POSIX file set imageFilePaths to every item in folder myImageFolderPath set imageFilePaths to (sort imageFilePaths by name) set imageFileNamesList to {} repeat with filePath in imageFilePaths set the end of imageFileNamesList to filePath's name end repeat end tell set mySortedJoinString to joinList(imageFileNamesList, ",") return mySortedJoinString end run --リスト結合 on joinList(theList, aDelimiter) set tmp to AppleScript's text item delimiters set AppleScript's text item delimiters to aDelimiter set theText to theList as string set AppleScript's text item delimiters to tmp return theText end joinList |
行けますね。楽でいいかも。