Archive for the 'NSRegularExpression' Category

2017/09/05 指定フォルダ以下のテキストファイルのファイル名冒頭についている数字から欠番を求める

ファイル名の先頭に1〜3桁の数字がついているテキストファイルに対して、指定フォルダ以下のものをすべて取得し、これらの数(連番)に抜けがないかをチェックするAppleScriptです。

filenames2.png

指定フォルダ以下に存在するテキストファイルをSpotlightで検索し、すべてのファイル名を取得してそこから先頭に存在する1〜3桁の数字を取得。全角文字が混入している場合にそなえて数字をすべて全角→半角変換。これらの数(おそらく連番)で途中に抜けがないかチェックします。

実行にはShane StanleyのAppleScript Libraries「BridgePlus」「MetaData Lib」のインストールが必要です。

連番リストの作成や全角→半角変換、Spotlight検索などをScript Librariesの機能に依存して書いています。また、集合(NSMutableSet)を扱えることで非常に高度な処理を完結に記述できています。処理速度も非常に高速です。

 127ファイルの連番チェック処理:0.113457024097 sec.
 449ファイルの連番チェック処理:0.381642997265 sec.

(結果はMacBook Pro Retina 2012 Core i7 2.66GHz+8GBでの所用時間)

AppleScript名:指定フォルダ以下のテキストファイルのファイル名冒頭についている数字から欠番を求める
– Created 2017-09-04 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use mdLib : script “Metadata Lib” version “1.0.0″
use bPlus : script “BridgePlus”
–http://piyocast.com/as/archives/4803

property SMSForder : a reference to current application’s SMSForder
property NSMutableArray : a reference to current application’s NSMutableArray
property NSMutableSet : a reference to current application’s NSMutableSet
property NSIndexSet : a reference to current application’s NSIndexSet
property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators
property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines
property NSRegularExpression : a reference to current application’s NSRegularExpression
property NSString : a reference to current application’s NSString

load framework –BridgePlus’s force framework loading command

–選択したフォルダ以下のPlain TextをすべてSpotlightで求める(POSIX path list)
set theFolder to (choose folder)
set aRes to retMissingNumberFromEachFiles(theFolder, {“public.plain-text”}) of me
–>  {}

on retMissingNumberFromEachFiles(theFolder, fileTypeList)
  set theFiles to mdLib’s searchFolders:{theFolder} searchString:“kMDItemContentType IN[c] %@” searchArgs:fileTypeList
  
if theFiles = {} then return
  
  
–取得したPOSIX Pathのリストからファイル名の部分のみ抽出
  
set anArray to NSMutableArray’s arrayWithArray:theFiles
  
set bArray to (anArray’s valueForKeyPath:“lastPathComponent”) as list
  
  
–各ファイルの名称の冒頭から1〜3桁 の数字を取り出して、全角–>半角変換を行いつつリストに追加
  
set nArray to NSMutableArray’s new()
  
repeat with i in bArray
    set j to contents of i
    
set aRes to (my findPattern:“^\\d{1,3}” inString:j)
    
if aRes is not equal to {} then
      set jj to (contents of first item of aRes) as string
      
set jj2 to (SMSForder’s transformedFrom:jj ICUTransform:“Fullwidth-Halfwidth” inverse:false) as integer
      (
nArray’s addObject:jj2)
    end if
  end repeat
  
  
–最大値、最小値をもとに連番リストを作成し、ファイル名から得られた配列データとの補集合を求める
  
set maxRes to (nArray’s valueForKeyPath:“@max.self”)’s intValue()
  
set minRes to (nArray’s valueForKeyPath:“@min.self”)’s intValue()
  
  
–最小値から最大値までの連番リスト作成
  
set theIndexSet to NSIndexSet’s indexSetWithIndexesInRange:{minRes, maxRes}
  
set theList to (SMSForder’s arrayWithIndexSet:theIndexSet) as list
  
  
set aSet to NSMutableSet’s setWithArray:theList
  
set bSet to NSMutableSet’s setWithArray:nArray
  
aSet’s minusSet:bSet –補集合
  
  
return (aSet’s allObjects() as list)
  
end retMissingNumberFromEachFiles

on findPattern:thePattern inString:theString
  set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list
  
set theResult to {}
  
set theNSString to NSString’s stringWithString:theString
  
  
repeat with i in theFinds
    set theRange to (contents of i)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as string
  end repeat
  
return theResult
end findPattern:inString:

★Click Here to Open This Script 

2017/09/02 Finder上で選択中のファイルの中に書かれているタイトルに基づいてリネーム

Finder上で選択中のテキストファイルの中に書かれているタイトルを正規表現による検索で検出して、ファイル名に指定するAppleScriptです。

技術的にはぜんぜん見るべき点がありませんが、そこそこ役立つ見本です(ありものをつなぎ合わせた書き捨てレベル)。

「小説を読もう」サイトからKnight’s and Magicのテキストを(テキストエンコーディングにUTF-8を指定して)ダウンロードしてきたら、

text_download.png

ファイル名が、

filename_before.png

のようになっていたので、これを、

filename_after.png

のようにリネームしたいと考え、各テキストファイル中に書かれているタイトル部分を、

file_contents.png

正規表現で検索して指定してみました。ほとんどテキストの1行目にタイトルが書かれているので、1行目の内容をファイル名にしてもよかったのですが、

knight_magic4.png

このように(↑)たまにイレギュラーなファイルが存在しているので、正規表現でサーチしています。

実際にテストしてみたところ、全角文字の「#」と半角文字の「#」が登場していたので、全角文字で検索して存在しないようであれば半角文字で再検索するようになっています。

数百個ぐらいのファイルの処理だと、このようにFinderからselectionを拾ってきても十分なところですが、数千個以上になると別の方法を考えるべきでしょう。

AppleScript名:Finder上で選択中のファイルの中に書かれているタイトルに基づいてリネーム
– Created 2017-09-01 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4797

property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators
property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines
property NSRegularExpression : a reference to current application’s NSRegularExpression
property NSString : a reference to current application’s NSString

tell application “Finder”
  set aSel to selection as alias list
  
if aSel = {} or aSel = “” then return
end tell

repeat with i in aSel
  set aStr to (read i as «class utf8»)
  
  
–全角のタイトルマークでタイトルを検索
  
set aList to retHeaders(aStr, “#”) of me
  
if aList is not equal to {} then
    set newName to (contents of first item of aList)
    
tell application “Finder”
      set name of i to newName & “.txt”
    end tell
  else
    –半角のタイトルマークでタイトルを検索
    
set bList to retHeaders(aStr, “#”) of me
    
if bList is not equal to {} then
      set newName to (contents of first item of bList)
      
tell application “Finder”
        set name of i to newName & “.txt”
      end tell
    else
      log {i as string}
    end if
  end if
end repeat

on retHeaders(aCon, aHeaderMark)
  set tList to {}
  
set regStr to “^” & aHeaderMark & “{1,6}[^” & aHeaderMark & “]*?$”
  
  
set headerList to my findPattern:regStr inString:aCon
  
repeat with i in headerList
    set j to contents of i
    
set regStr2 to “^” & aHeaderMark & “{1,6}[^” & aHeaderMark & “]*?”
    
set headerLevel to length of first item of (my findPattern:regStr2 inString:j)
    
set the end of tList to (text (headerLevel + 1) thru -1 in j)
  end repeat
  
  
return tList
end retHeaders

on findPattern:thePattern inString:theString
  set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list – so we can loop through
  
set theResult to {} – we will add to this
  
set theNSString to NSString’s stringWithString:theString
  
repeat with i from 1 to count of items of theFinds
    set theRange to (item i of theFinds)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as string
  end repeat
  
return theResult
end findPattern:inString:

★Click Here to Open This Script 

2017/08/16 Markdown書類から見出し(Header)行を抽出_v2

指定のMarkdown書類から正規表現で見出し(Header)行を抽出し、見出しレベルと見出しテキストを出力するAppleScriptです。

指定フォルダ下のすべてのMarkdown書類をSpotlight処理で抽出し、各書類をこのScriptで処理。まとめて見出しレベルを統計処理してみました。複数の書籍で見出しレベルの分布などを比較するなど、割と重そうな処理をあっさり(数秒のオーダーで)処理できたのでよかったと思います。

AppleScript名:Markdown書類から見出し(Header)行を抽出_v2
– Created 2017-08-12 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4776

property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators
property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines
property NSRegularExpression : a reference to current application’s NSRegularExpression
property NSString : a reference to current application’s NSString

set aFile to choose file of type {“net.daringfireball.markdown”} –Markdown書類のUTI
set aStr to (read aFile as «class utf8»)
set aList to retHeaders(aStr) of me
–>  {{3, “choose file, choose folderで選んだ対象の名称変更”}, {3, “choose folderで選んだフォルダ内のファイルの名称変更”}, {3, “Finder上で選んだ(selection)ファイルの名称変更”}, {3, “POSIX pathのファイルの名称変更”}, {3, “ファイルを移動させたうえで名称変更”}, {3, “要注意事項(超重要、生死にかかわる)”}}

on retHeaders(aCon)
  set tList to {}
  
set regStr to “^#{1,6}[^#]*?$”
  
  
set headerList to my findPattern:regStr inString:aCon
  
repeat with i in headerList
    set j to contents of i
    
set regStr2 to “^#{1,6}[^#]*?”
    
set headerLevel to length of first item of (my findPattern:regStr2 inString:j)
    
set the end of tList to {headerLevel, text (headerLevel + 1) thru -1 in j}
  end repeat
  
  
return tList
end retHeaders

on findPattern:thePattern inString:theString
  set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list – so we can loop through
  
set theResult to {} – we will add to this
  
set theNSString to NSString’s stringWithString:theString
  
repeat with i from 1 to count of items of theFinds
    set theRange to (item i of theFinds)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as string
  end repeat
  
return theResult
end findPattern:inString:

★Click Here to Open This Script 

2016/12/17 ネットワークデバイスからactiveなものだけをピックアップする

ネットワークデバイスから、activeなものだけをピックアップするAppleScriptです。

DHCPのネットワークアドレスのリフレッシュを試みたときに、接続中のネットワークデバイス名が必要になるので、試しに作ってみました。

試作品レベルなので、実用性についてはいろいろ問題があります。

まず、ネットワーク接続が切れているとまともに動かないので、ネットワーク接続確認は別途行なっておく必要があります。

さらに、複数のネットワークインタフェース(USB-Ethernetアダプタと本体内蔵WiFiとか)がアクティブになっている場合の結果が正しいことは保証しません(まだ、このあたりの詰めが必要)。

本プログラムでは、複数返ってきた場合には最初の項目を返してくるので、自分のマシン環境でも「これはいかがなものか」という状態です(USB-Ethernetアダプタで有線接続しつつ、位置情報を取得するためにWiFiをオンにすることがあるので)。

正規表現の鬼のような人だと、もう少し美しく短いプログラムにまとめられると思います。自分が短く書くために選んだ道具はtext item delimiters。

text item delimitersに複数(2よりも多い複数)の項目を指定できるので、あらかじめ各デバイス情報の1行目だけをピックアップしておき、これをtext item delimitersに指定してifconfigの処理結果をリスト化し、欠けた各デバイス情報の1行目を順次補っていくという、かなり頭のおかしなプログラムです。

AppleScript名:ネットワークデバイスからactiveなものだけをピックアップする
– Created 2016-12-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4367

set aDevRes to getActiveNetworkInterfaceDeviceName() of me
set aDevName to devName of aDevRes
–>  ”en3″

–ifconfigからactiveなデバイス名だけをピックアップする
on getActiveNetworkInterfaceDeviceName()
  set aRes to (do shell script “ifconfig”)
  
set aList to paragraphs of aRes
  
  
set devNameList to {}
  
repeat with i in aList
    set j to contents of i
    
set bRes to regexMatches(j, “^[^\\t]*”) of me
    
if bRes is not equal to {{“”}} then
      set the end of devNameList to contents of item 1 of item 1 of bRes
    end if
  end repeat
  
  
–Parse ifconfig results by device name list
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to devNameList –かなりトリッキー
  
set tmpList to text items of aRes
  
set AppleScript’s text item delimiters to curDelim
  
  
–initialize
  
set bList to rest of tmpList –remove blank item at top
  
set aCount to 1
  
set aResList to current application’s NSMutableArray’s alloc()’s init()
  
  
repeat with i in bList
    set aCon to contents of i
    
set aDat to contents of item aCount of devNameList
    
–Device Name
    
set aDevName to retStrFromTopToAstr(aDat, “:”) of me
    
    
set bCon to paragraphs 2 thru -1 of aCon
    
set cCon to aDat & retDelimedText(bCon, return) of me
    
    
–Activeなdeviceを抽出するための条件付け
    
set activeF1 to cCon contains “status: active”
    
set activeF2 to cCon does not contain “media: autoselect (<unknown type>)”
    
    
set aRec to (current application’s NSDictionary’s dictionaryWithDictionary:{devName:aDevName, isActive:(activeF1 and activeF2), ifconfigRes:cCon})
    (
aResList’s addObject:aRec)
    
set aCount to aCount + 1
  end repeat
  
  
set activeIF to filterRecListByLabel(aResList, “isActive == [c]%@”, {true}) of me
  
(*
  {{devName:”en3″, ifconfigRes:”en3: flags=8863 mtu 1500\toptions=4\r\tether XX:Xx:xX:Xx:XX:xX \r\tinet6 xxXX::XxX:XXXX:XXXx:Xxxx%en3 prefixlen 64 secured scopeid 0×4 \r\tinet 192.168.0.5 netmask 0xffffff00 broadcast 192.168.0.255\r\tinet6 XXXx:XX:XXxX:X:xxX:XXXX:XXXx:XXxX prefixlen 64 autoconf secured \r\tinet6 XXXx:XX:XXxX:X:XXx:XXXX:XXXx:xXXx prefixlen 64 autoconf temporary \r\tnd6 options=201 \r\tmedia: autoselect (100baseTX )\r\tstatus: active”, isActive:true}}
*)
  return first item of activeIF –Multiple Device Name list may return. Now, I choose one.
end getActiveNetworkInterfaceDeviceName

–http://qiita.com/szk-3/items/8bbe841eb0295caee6b0
on regexMatches(aText as text, pattern as text)
  set regularExpression to current application’s NSRegularExpression’s regularExpressionWithPattern:pattern options:0 |error|:(missing value)
  
set aString to current application’s NSString’s stringWithString:aText
  
set matches to regularExpression’s matchesInString:aString options:0 range:{location:0, |length|:aString’s |length|()}
  
set matchResultList to {}
  
repeat with match in matches
    set mRes to {}
    
repeat with i from 0 to (match’s numberOfRanges as integer) - 1
      set end of mRes to (aString’s substringWithRange:(match’s rangeAtIndex:i)) as text
    end repeat
    
set end of matchResultList to mRes
  end repeat
  
return matchResultList
end regexMatches

–リストを指定デリミタを入れつつテキスト化
on retDelimedText(aList, aDelim)
  set aText to “”
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retDelimedText

–先頭から指定キャラクタまでを抽出して返す
on retStrFromTopToAstr(aStr, aTarg)
  set aLen to length of aTarg
  
set anOffset to offset of aTarg in aStr
  
set bStr to text 1 thru (anOffset - aLen) of aStr
  
return bStr
end retStrFromTopToAstr

–リストに入れたレコードを、指定の属性ラベルの値で抽出(predicateとパラメータを分離)
on filterRecListByLabel(aRecList, aPredicate, aParam)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate argumentArray:aParam
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to filteredArray as list
  
return bList
end filterRecListByLabel

★Click Here to Open This Script