Archive for the '正規表現(regex)' Category

2016/12/20 OgreKit.frameworkを呼び出して正規表現処理を行う

正規表現の機能を提供するOgreKit.framework(オウガキット)をAppleScriptから呼び出すじっけんです。
ogrekitlogo.png

ASOCを使い始めて真っ先にテストしていたのですが、当時はまだObjective-CからAppleScriptへの「翻訳」のスキルが高くなかったので動くようにできませんでした。さすがに今だと問題なく翻訳できます。

本AppleScriptを構文確認・実行するには、OgreKit.frameworkを~/Library/Frameworksフォルダに入れておく必要があります。ただし、OgreKit自体GitHubで配布されているソースをビルドするといいのか、どこかで配布されているバイナリを拾ってきたのか、入手経路についてはあまり記憶が定かではありません(けっこう放置していたので)。ちなみに、確認はバージョン2.0で行いました。

OgreKitをはじめCocoa+Objective-Cから使える正規表現のフレームワークは割といろいろ試していますが、テキストの抽出や置換が目的であって、GUIアプリケーション上のオブジェクトをこれで抽出できるというものではありません。そこはフィルタ参照を用いることになります。

AppleScript名:OgreKitで文字列中の正規表現にマッチした部分を順番に得る
– Created 2016-12-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “OgreKit”
–http://piyocast.com/as/archives/4368
–http://sonoisa.github.io/ogrekit/HowToUse2.html

set regEx to current application’s OGRegularExpression’s regularExpressionWithString:“a[^a]*a”
set enum to regEx’s matchEnumeratorInString:“alphabetagammadelta”

set aResArray to current application’s NSMutableArray’s alloc()’s init()
repeat
  set aMatch to enum’s nextObject()
  
if aMatch = missing value then exit repeat
  
set hitStr to aMatch’s matchedString()
  
aResArray’s addObject:hitStr
end repeat

return aResArray as list
–>  {”alpha”, “aga”, “adelta”}

★Click Here to Open This Script 

AppleScript名:OgreKitで文字列を正規表現にマッチした部分で分割する
– Created 2016-12-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “OgreKit”
–http://piyocast.com/as/archives/4368
–http://sonoisa.github.io/ogrekit/HowToUse2.html

set regEx to current application’s OGRegularExpression’s regularExpressionWithString:“\\s*,\\s*”
set dRes to (regEx’s splitString:“36.5C, 3.8C, -195.8C”) as list
–> {”36.5C”, “3.8C”, “-195.8C”}

★Click Here to Open This Script 

AppleScript名:OgreKitで文字列中の正規表現にマッチした部分すべてを置換する
– Created 2016-12-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “OgreKit”
–http://piyocast.com/as/archives/4368
–http://sonoisa.github.io/ogrekit/HowToUse3.html

set regEx to current application’s OGRegularExpression’s regularExpressionWithString:“a[^a]*a”
set dRes to (regEx’s replaceAllMatchesInString:“alphabetagammadelta” withString:“(\\0)”) as string
–>  ”(alpha)bet(aga)mm(adelta)”

★Click Here to Open This Script 

AppleScript名:OgreKitでNSStringに対して検索を行い、最初にマッチした範囲を返す
– Created 2016-12-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “OgreKit”
–http://piyocast.com/as/archives/4368
–http://sonoisa.github.io/ogrekit/HowToUse6.html

set aStr to current application’s NSString’s stringWithString:“alphabetagammadelta”
set matchedRange to aStr’s rangeOfRegularExpressionString:“a[^a]*a”
return matchedRange as record
–>  {location:0, length:5}

★Click Here to Open This Script 

AppleScript名:OgreKitでNSMutableStringに対して検索・置換を行う
– Created 2016-12-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “OgreKit”
–http://piyocast.com/as/archives/4368
–http://sonoisa.github.io/ogrekit/HowToUse6.html

set aStr to current application’s NSMutableString’s stringWithString:“alphabetagammadelta”
aStr’s replaceOccurrencesOfRegularExpressionString:“a[^a]*a” withString:“(\\0)” options:1 range:(current application’s NSMakeRange(0, aStr’s |length|()))
aStr as string
–>  ”(alpha)bet(aga)mm(adelta)”

★Click Here to Open This Script 

AppleScript名:OgreKitで指定テキストファイルの改行コードを統一する
– Created 2016-12-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “OgreKit”
use textParser : script “japaneseTextEncodingDetector”
–http://piyocast.com/as/archives/4368
–http://sonoisa.github.io/ogrekit/HowToUse5.html

property OgreNonbreakingNewlineCharacter : -1 –(無改行)
property OgreUnixNewlineCharacter : 0 –LF(Unix)
property OgreLfNewlineCharacter : 0 –LF(Unix)
property OgreMacNewlineCharacter : 1 –CR(Mac)
property OgreCrNewlineCharacter : 1 –CR(Mac)
property OgreWindowsNewlineCharacter : 2 –CR+LF(Windows)
property OgreCrLfNewlineCharacter : 2 –CR+LF(Windows)

set aFile to POSIX path of (choose file)
set aText to readJapanesTextFileWithGuessingEncoding(aFile) of textParser –日本語テキストエンコーディング自動判定ライブラリで判定
set cText to current application’s OGRegularExpression’s replaceNewlineCharactersInString:aText withCharacter:OgreLfNewlineCharacter

★Click Here to Open This Script 

2015/10/13 数字以外を削除して返す

与えられた文字列のうち、数字以外を削除して返すAppleScriptです。

正規表現で数字以外をヌルに置換して返します。スペースを許容するものと、スペースと改行を許容するものも作っておきました。

後で加工することを考えると、地味にスペースも取り出せると処理が楽だったので、、、

AppleScript名:ASOCで数字以外を削除して返す
– Created 2015-10-13 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aStr to "<< 98158999992
>>"

set bStr to returnNumberCharsOnly(aStr)
–>  "98158999992"

set cStr to returnNumberCharsAndSpaceOnly(aStr)
–> " 98158999992 "

set dStr to returnNumberCharsAndSpaceAndReturnOnly(aStr)
–>
(*
" 98158999992
"
*)

on returnNumberCharsOnly(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:"[^0-9]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end returnNumberCharsOnly

on returnNumberCharsAndSpaceOnly(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:"[^0-9 ]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end returnNumberCharsAndSpaceOnly

on returnNumberCharsAndSpaceAndReturnOnly(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:"[^0-9
]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  return anNSString as text
end returnNumberCharsAndSpaceAndReturnOnly

★Click Here to Open This Script 

2015/09/28 NSPredicateによる正規表現を利用した部分一致抽出

Cocoaの機能を用いて、リストのレコードから正規表現による要素の抽出を行うAppleScriptです。実行にはShane StanleyのAppleScript Library「BridgePlus」のインストールが必要になります。

NSPredicateや複数条件を併記するNSCompoundPredicateで、リストに入ったレコードを抽出するのが楽すぎて、もはやこれなしにデータの抽出ができなくなりつつあります。ゴリゴリにPure AppleScriptで記述できないこともないのですが、NSPredicateが使えるだけでもASOCを覚える価値があると思えるほどです。

そんな中、徐々に記述レベルが上がってくると、無茶な抽出にもチャレンジしてみたくなるもので・・・

  「98」で始まる8桁の数字が入った要素をリストアップ。ただし、途中に出現するパターンも含む

という抽出にチャレンジしてみました(仕事で必要だったもので)。完全一致ならなんとか書けるのですが、上記の条件の文字列を「含む」ものまで抽出することを考えると、とたんに難易度が上がります。

結局、AppleのPredicate Programming Guideを総チェックして、「.*」で囲めば「条件に合うものを含む」という状態を抽出できることを見つけました(プログラミングではなくて、情報収集とかパズルのレベルですね)。

AppleScript名:ASOCでNSPredicateによる正規表現を併用した抽出
– Created 2015-09-28 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus” —Shane Stanley’s BridgePlus

set sampleList to {{textData:“Piyomaru”, uID:1}, {textData:“Xx Piyomaru x”, uID:2}, {textData:“xxxxx 11111111 98 x xxxxxxxx.”, uID:3}, {textData:“98x Xxxxxx (xx xxxxxxxxxx)”, uID:4}, {textData:“< < 98158113 >>”, uID:5}, {textData:“#98158084 Xxxxx Xxxxx xxxx”, uID:6}, {textData:“#98158084 Xxxxx Xxxxx xxxx”, uID:7}, {textData:“Office # 98158107″, uID:8}, {textData:“ID#98158087″, uID:9}, {textData:“98158089″, uID:10}, {textData:“00158098″, uID:11}}

–全文一致で抽出
set aRes to my filterRecListByLabel1(sampleList, “textData == ’Piyomaru’”)
–>  {​​​​​{​​​​​​​textData:”Piyomaru”, ​​​​​​​uID:1​​​​​}​​​}

–部分一致で抽出
set bRes to my filterRecListByLabel1(sampleList, “textData contains ’Piyomaru’”)
–>  {​​​​​{​​​​​​​textData:”Piyomaru”, ​​​​​​​uID:1​​​​​}, ​​​​​{​​​​​​​textData:”Xx Piyomaru x”, ​​​​​​​uID:2​​​​​}​​​}

–正規表現で抽出(8桁の数字)
set cRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’\\\\d{8}’”)
–>  {​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}, ​​​​​{​​​​​​​textData:”00158089″, ​​​​​​​uID:11​​​​​}​​​}

set dRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’98\\\\d{6}’”)
–>  {​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

set eRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*98??????*’”)
–>  {​​​​​{​​​​​​​textData:”xxxxx 11111111 98 x xxxxxxxx.”, ​​​​​​​uID:3​​​​​}, ​​​​​{​​​​​​​textData:”98x Xxxxxx (xx xxxxxxxxxx)”, ​​​​​​​uID:4​​​​​}, ​​​​​{​​​​​​​textData:”< < 98158113 >>”, ​​​​​​​uID:5​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:6​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:7​​​​​}, ​​​​​{​​​​​​​textData:”Office # 98158107″, ​​​​​​​uID:8​​​​​}, ​​​​​{​​​​​​​textData:”ID#98158087″, ​​​​​​​uID:9​​​​​}, ​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

set fRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*\”98\”[0-9][0-9][0-9][0-9][0-9][0-9]*’”) –Oops!!
–>  {}

set gRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]*’”) –Oops!!
–>  {}

set hRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’.*[98]\\\\d{6}.*’”) –OK!!
–>  {​​​​​{​​​​​​​textData:”< < 98158113 >>”, ​​​​​​​uID:5​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:6​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:7​​​​​}, ​​​​​{​​​​​​​textData:”Office # 98158107″, ​​​​​​​uID:8​​​​​}, ​​​​​{​​​​​​​textData:”ID#98158087″, ​​​​​​​uID:9​​​​​}, ​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to ASify from filteredArray as list
  
return bList
end filterRecListByLabel1

★Click Here to Open This Script 

2015/09/14 ASOCで文字種別を判定する v2

Cocoaの機能を用いて、文字種別を判定するAppleScriptです。

記号(Symbol)のチェックと、製品コードなどのルールを供給するとそのとおりの文字の並びになっているかをチェックするルーチンを加えました。

これまでに作ってきた同種のルーチンは以下のようになります。

コードのチェック
簡易マクロ展開つきのコードチェック v1

わざわざ文字を分解するよりも、ルールから正規表現の文字列を組み立てて、そのとおりになっているかのチェックを行うほうがよいかも・・・とは考えたのですが、どの桁にエラーがあるかを確認するためにはこのような仕様のほうがよいだろうか、と思うものです。

AppleScript名:ASOCで文字種別を判定する v2
– Created 2015-09-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–http://core-tech.jp/gijutsublog/2014/06/23/10505

set aRes to my chkStrings:“0123-45-678A” asRule:“9999-99-999X”
–>  {​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true​​​}
set bRes to my chkStrings:“0123-45-d2aA” asRule:“9999-99-999X”
–>  {​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​false, ​​​​​true, ​​​​​false, ​​​​​true​​​}

–指定したルールのとおりの文字種の並びになっているか?
on chkStrings:aStr asRule:aRuleStr
  set aList to characters of aStr
  
set rList to characters of aRuleStr
  
if (length of aList is not equal to length of rList) then return false
  
  
set chkList to {}
  
repeat with i from 1 to (length of aList)
    set j1 to contents of item i of aList
    
set j2 to contents of item i of rList
    
    
if j2 = “9″ then
      set j3 to (my chkNumeric:j1)
    else if j2 = “X” then
      set j3 to (my chkAlphabet:j1)
    else if j2 is in {“$”, “\”", “!”, “~”, “&”, “=”, “#”, “[”, “]”, “.”, “_”, “-”, “+”, “`”, “|”, “{”, “}”, “?”, “%”, “^”, “*”, “/”, “’”, “@”, “-”, “/”, “:”, “;”, “(”, “)”, “,”} then
      set j3 to (my chkSymbol:j1)
    end if
    
set the end of chkList to j3
  end repeat
  
return chkList
end chkStrings:asRule:

– アルファベットのみか
on chkAlphabet:checkString
  set aStr to current application’s NSString’s stringWithString:checkString
  
set allCharSet to current application’s NSMutableCharacterSet’s alloc()’s init()
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of “a”, 26))
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of “A”, 26))
  
set aBool to my chkCompareString:aStr baseString:allCharSet
  
return aBool as boolean
end chkAlphabet:

–数字のみか
on chkNumeric:checkString
  set digitCharSet to current application’s NSCharacterSet’s characterSetWithCharactersInString:“0123456789″
  
set ret to my chkCompareString:checkString baseString:digitCharSet
  
return ret as boolean
end chkNumeric:

–アルファベットと数字のみか
on chkAlphaNumeric:checkString
  set alnumCharSet to current application’s NSCharacterSet’s alphanumericCharacterSet()
  
set ret to my chkCompareString:checkString baseString:alnumCharSet
  
return ret as boolean
end chkAlphaNumeric:

–アルファベットと数字と記号のみか
on chkAlphaNumericSymbol:checkString
  set muCharSet to current application’s NSCharacterSet’s alphanumericCharacterSet()’s mutableCopy()
  
muCharSet’s addCharactersInString:“$\”!~&=#[]._-+`|{}?%^*/’@-/:;(),”
  
set ret to my chkCompareString:checkString baseString:muCharSet
  
return ret as boolean
end chkAlphaNumericSymbol:

–記号のみか
on chkSymbol:checkString
  set muCharSet to current application’s NSCharacterSet’s alloc()’s init()
  
muCharSet’s addCharactersInString:“$\”!~&=#[]._-+`|{}?%^*/’@-/:;(),”
  
set ret to my chkCompareString:checkString baseString:muCharSet
  
return ret as boolean
end chkSymbol:

–全角文字が存在するか
on chkMultiByteChar:checkString
  set aStr to current application’s NSString’s stringWithString:checkString
  
set aRes to aStr’s canBeConvertedToEncoding:(current application’s NSASCIIStringEncoding)
  
return (aRes as boolean)
end chkMultiByteChar:

on chkCompareString:checkString baseString:baseString
  set aScanner to current application’s NSScanner’s localizedScannerWithString:checkString
  
aScanner’s setCharactersToBeSkipped:(missing value)
  
aScanner’s scanCharactersFromSet:baseString intoString:(missing value)
  
return (aScanner’s isAtEnd()) as boolean
end chkCompareString:baseString:

★Click Here to Open This Script 

2015/09/09 続・伏字文字列を作成する

ここに掲載するScriptのMACアドレスやらシリアル番号やらを伏字にするため「だけに」作ったAppleScript。1行のテキストのうちの数文字だけ伏字にするため、速度も何も重視していなかったほぼ作り捨てレベルのこのScriptに技術的な課題を見出してしまった人物がいます。

Shane Stanleyです。なぜだ〜(^ー^;;。

自分が作ったPure AppleScript版(A)、Shaneによる英数字しか考慮していないASOC版(B)、Shaneによる2バイト文字も考慮したASOC版(C)の3つのバージョンが登場。

実行速度は、

 (A):◼️◼️ 0.0001976 sec
 (B):◼️◼️◼️◼️ 0.00038 sec
 (C):◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️ 0.001 sec

ぐらい違います(◼️が少ない方が速い)。

v3vsv4.png

伏字と考えると分かりにくいですが(日本語の伏字は、「伏せてもなぜか意味が分かる、ハイレベルな文芸活動」だったりするので)、「文字グループを指定した文字置換」ととらえると、技術的にツッコミを入れるべき対象に見えるかもしれません。

AppleScript名:伏字文字列を作成する v3
– Created 2015-09-09 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aStr to “This is my secret information.”
set bStr to makeUnprintableChars(aStr)
–>  ”Xxxx xx xx xxxxxx xxxxxxxxxxx.”

on makeUnprintableChars(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[A-Z0-9]” withString:“X” options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[a-z]” withString:“x” options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end makeUnprintableChars

★Click Here to Open This Script 

AppleScript名:伏字文字列を作成する v4
– Created 2015-09-09 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aStr to “これは秘密のInformationです。” –Alphabet & Numeric以外でも置換可能
set bStr to makeUnprintableChars(aStr)
–>  ”xxxxxxXxxxxxxxxxxxx。”

on makeUnprintableChars(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[\\p{Lu}\\p{Lt}\\p{Nd}]” withString:“X” options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[\\p{Ll}\\p{Lm}\\p{Lo}]” withString:“x” options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end makeUnprintableChars

★Click Here to Open This Script 

2014/11/22 Keynote 6.5で各スライドのタイトル、マスタースライド名を取得してデータ化

Keynote 6.5で作成中の書類(開発中のシステムの仕様書)から、各ページタイトルを取得。さらに各スライド(ページ)のマスタースライド名を取得しておいて、目次の文字の大きさに違いをつけようとして、データを作成するAppleScriptの試作品です。

実際に、一般的なアプリケーションであるKeynoteをコントロールしつつ、データ処理をASOCベースのサブルーチンで行わせてみました。

40ページ強あるKeynoteのデータの処理を1秒以下で行えます(MacBook Pro Retina mid 2012)。

また、各スライド(ページ)のマスタースライド名については、出現頻度をカウントし、出現頻度の少ないものが各章のトビラであると仮定して、各タイトルにレベル設定(トビラページは1、通常ページは2)を行っています。

実際に、OmniGraffleではこうした処理(各ページのタイトルを求めて、目次ページを動的に作成してすべての項目に実際のページへのリンクを設定)をすべてAppleScriptで行っていますが(PDFに書き出したときに便利)・・・OmniGraffleで行うよりも、(ユーザーの多い)Keynote上で行えたほうが便利です。

Keynote 6.5もけっこうAppleScript対応機能が向上してきてはいるのですが、たとえばテキストオブジェクトを生成しても・・・左寄せ/センタリング/右寄せを指定できないとか、フォントと文字サイズも設定できないとか(ここはまだ試行錯誤の余地あり)、他のスライド(ページ)へのリンク設定が行えないなど、実践的な処理を記述するためには「いまひとつ」な印象です。

とはいえ、Keynote 6.5のAppleScript用語辞書を見ていると、オブジェクトにScript Labelを(将来的に)設定できるように考えられているなど、楽しみです(グラフ方面は手付かずだったり)。

Keynoteには、早く完全体になってほしいところです(永遠に未完成ということはないと思いたいですが、なかなか進捗しないですね)。

ちなみに、Keynoteへのtellブロック内でcocoaの機能にアクセスするコードを直接書くと、実行時にScript Editorがクラッシュするなどなかなかたいへんでした。ASOCのコードは通常のアプリケーションへのアクセスと分けたほうが得策のようです。

AppleScript名:keynote_test
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

property sPage : 3 –1ページ目が表紙、2ページ目が目次として3ページ目以降を処理対象とする

–最前面のドキュメントを取得する
tell application “Keynote”
  set aDocRef to front document
end tell

set tList to retKeynoteSlideTitleFrom(3, aDocRef) of me
–> {”システム概要”, “ハードウェア構成概要”, “ソフトウェア構成概要”,….}

–ドキュメント中の3ページ以降のslideの各マスタースライドの名称を取得して、各マスタースライドの出現頻度を取得して昇順ソート
set bList to retKeynoteSlideBaseSlideNameFrom(sPage, aDocRef) of me
–> {{”タイトル(中央)”, 8}, {”タイトル(上)”, 32}}–{マスタースライド名, 登場頻度カウント}

set {masterList, freqList} to conv2dListTo1dLists(bList) of me
–> {{”タイトル(中央)”, “タイトル(上)”}, {8, 32}}

set tOutList to {}

tell application “Keynote”
  tell aDocRef
    repeat with i from sPage to (count every slide)
      tell slide i
        –Title
        
set aIndex to (i - sPage + 1)
        
set aTitle to contents of item aIndex of tList
        
        
–Base Slide
        
set aBaseName to name of base slide
        
set aLevel to offseOfList(masterList, aBaseName) of me
        
        
set the end of tOutList to {aTitle, aLevel}
        
      end tell
    end repeat
  end tell
end tell

return tOutList
–> {{”システム概要”, 1}, {”ハードウェア構成概要”, 2}, {”ソフトウェア構成概要”, 2}, {”プログラム概要”, 2}, {”プログラム呼称一覧”, 2}, ….}
–1=マスタースライドが”タイトル(中央)”
–2=マスタースライドが “タイトル(上)”

–List中の指定項目の出現位置を返す(複数ヒットした場合には最初の項目番号)。1はじまりのAS仕様のインデックスを使用
on offseOfList(aList, anItem)
  set aResList to (current application’s SMSFord’s indexesOfItem:anItem inArray:(aList) inverting:false) as list
  
set aRes to (first item of aResList)
  
return (aRes + 1)
end offseOfList

–指定ページ(slide)から末尾までの各ページのTitleを取得して返す
on retKeynoteSlideTitleFrom(startPageNum as integer, aDocRef)
  
  
set tList to {}
  
  
try
    –全ページ(slide)のtitleを一括で取得するのが一番高速
    
tell application “Keynote”
      tell aDocRef
        set sCount to count every slide
        
        
if sCount < startPageNum then error
        
if startPageNum < 1 then return {}
        
        
set tList to object text of default title item of every slide
      end tell
    end tell
  on error
    return {}
  end try
  
  
–取得したタイトルから、前後の改行、前後の空白、および途中に入っている改行を削除する
  
set t2List to {}
  
repeat with i in tList
    set j to contents of i
    
set jj to cleanUpText(j) of me
    
set jj to cleanUpCRLF(jj) of me
    
set the end of t2List to jj
  end repeat
  
  
  
  
–一括取得した内容を適宜加工して返すのがベスト
  
set ttList to contents of items startPageNum thru -1 of t2List
  
  
return ttList
  
end retKeynoteSlideTitleFrom

–文字列の前後の改行と空白文字を除去
on cleanUpText(someText)
  set theString to current application’s NSString’s stringWithString:someText
  
set theString to theString’s stringByReplacingOccurrencesOfString:” +” withString:” “ options:(current application’s NSRegularExpressionSearch) range:{location:0, |length|:length of someText}
  
set theWhiteSet to current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()
  
set theString to theString’s stringByTrimmingCharactersInSet:theWhiteSet
  
return theString as text
end cleanUpText

–文字置換
on repChar(aStr, targStr, repStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr
  
set cString to (bString’s ASify()) as string
  
return cString
end repChar

–指定文字列からCRLFを除去
on cleanUpCRLF(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:(string id 10) withString:“” –remove LF
  
set cString to bString’s stringByReplacingOccurrencesOfString:(string id 13) withString:“” –remove CR
  
set dString to (cString’s ASify()) as string
  
return dString
end cleanUpCRLF

–2D Listを1D Listに変換
on conv2dListTo1dLists(aList as list)
  
  
set newList to {}
  
set aLen to length of first item of aList
  
  
repeat aLen times
    set the end of newList to {}
  end repeat
  
  
  
repeat with i in aList
    repeat with ii from 1 to aLen
      set the end of item ii of newList to (item ii of i)
    end repeat
  end repeat
  
  
return newList
  
end conv2dListTo1dLists

–指定ページ(slide)から末尾までの各ページのbase slide名を取得してユニーク化して返す
on retKeynoteSlideBaseSlideNameFrom(startPageNum, aDocRef)
  
  
set aList to {}
  
  
tell application “Keynote”
    tell aDocRef
      set sCount to count every slide
      
if sCount < startPageNum then error
      
      
repeat with i from startPageNum to sCount
        tell slide i
          set aBase to name of base slide
          
set the end of aList to aBase
        end tell
      end repeat
      
    end tell
  end tell
  
  
  
–結果をユニーク化する
  
set aArray to current application’s NSArray’s arrayWithArray:aList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
  
set bList to bArray’s ASify() as list
  
–> {1, 1.1, 2, 3, 4}
  
  
  
–Base Slide名の出現頻度を調べる
  
set cList to {}
  
  
repeat with i in bList
    set aName to contents of i
    
set aCount to countSpecifiedItem(aList, aName) of me
    
set the end of cList to {aName, aCount}
  end repeat
  
  
  
–出現頻度(aCount)をキーにして昇順ソート
  
set dList to sort2DList(cList, 2, {true}) of me
  
  
return dList
  
end retKeynoteSlideBaseSlideNameFrom

–リスト中の指定項目の出現回数を返す
on countSpecifiedItem(aList, countItem)
  set aRes to (current application’s SMSFord’s indexesOfItem:countItem inArray:aList inverting:false) as list
  
set aCount to length of aRes
  
return aCount
end countSpecifiedItem

–2D Listをソート
on sort2DList(aList as list, sortIndexes as list, sortOrders as list)
  
  
–index値をAS流(アイテムが1はじまり)からCocoa流(アイテムが0はじまり)に変換
  
set newIndex to {}
  
repeat with i in sortIndexes
    set j to contents of i
    
set j to j - 1
    
set the end of newIndex to j
  end repeat
  
  
–Sort TypeのListを作成(あえて外部から指定する内容でもない)
  
set sortTypes to {}
  
repeat (length of sortIndexes) times
    set the end of sortTypes to “compare:”
  end repeat
  
  
–Sort
  
set resList to (current application’s SMSFord’s subarraysIn:(aList) sortedByIndexes:newIndex ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
  
  
return resList
  
end sort2DList

★Click Here to Open This Script 

2014/11/22 リストから抽出(asoc)

リストから条件に合う項目だけを抽出するAppleScriptです。

こういう感じで1D Listだけでなく2D Listを抽出できると便利でしょう。ああ、2D Listの抽出をやりたい、、、

AppleScript名:listの項目をフィルタリング(項目文字長)
– Created 2014-11-21 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aList to {“piyomaru”, “Piyomaru Software”, “Naganoya”, “Takaaki”, “MacBook Pro Retina mid 2012″}

set bList to filterListUsingPredicate(aList, “length > 8″) –文字列長が8文字より長い項目を返す
–> {”MacBook Pro Retina mid 2012″, “Piyomaru Software”}

set cList to filterListUsingPredicate(aList, “SELF MATCHES ’.*e$’”) –正規表現で末尾が”e”
–> {”Piyomaru Software”}

set dList to filterListUsingPredicate(aList, “SELF LIKE ’piyo*’”)
–> {”piyomaru”}

set eList to filterListUsingPredicate(aList, “SELF LIKE[c] ’piyo*’”)
–> {”piyomaru”, “Piyomaru Software”}

set fList to filterListUsingPredicate(aList, “SELF CONTAINS[c] ’Piyo’”)
–> {”piyomaru”, “Piyomaru Software”}

on filterListUsingPredicate(aList as list, aPredicateStr as string)
  –ListからNSArrayへの型変換
  
set setKey to current application’s NSMutableSet’s setWithArray:aList
  
  
–抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicateStr
  
set aRes to (setKey’s filteredSetUsingPredicate:aPredicate)
  
set bRes to aRes’s allObjects()
  
  
–NSArrayからListに型変換して返す
  
set cRes to (bRes’s ASify()) as list
  
return cRes
end filterListUsingPredicate

★Click Here to Open This Script 

2014/11/22 レコードのリストから抽出(asoc)

レコードのリスト(List化したRecord)から、条件に合う項目を抽出するAppleScriptです。

本来、AppleScriptの処理系だけで、

  set aList to {{aName:”ぴよまる”, weight:70}, {aName:”ぴよこ”, wight:60}}

のようなリスト化したレコードをフィルタ参照(set bList to every record whose cell “aName” is equal to “ぴよまる” とか)で抽出できるべきですが、長年搭載されてきませんでした。

こうした処理をAppleScriptで実現するためには、FileMaker Proなどのデータベースを併用するか、OS標準搭載のDataBase Events(フィルタ参照で該当データを抽出する専用のDBシステム。ソート機能がない)を利用することに(ASだけでゴリゴリ記述するという方向性もありますが、、、)。

さすがにDataBase Eventsを使うぐらいなら、ASOCで記述したほうが簡単です。

数百万件や数千万件の規模のデータを扱うにはFileMaker Proなどのデータベースを併用すべきだと思いますが、十万件ぐらいの規模のデータであればAppleScriptだけで便利にデータ処理できてよさそうです。

AppleScript名:asoc_レコードのリストから抽出
– Created 2014-11-21 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aRecList to {{aName:“piyoko”, aVal:100}, {aName:“piyomaru”, aVal:80}, {aName:“piyoo”, aVal:10}, {aName:“Gundamo”, aVal:10}}

set bList to filterRecListByLabel(aRecList, “aName like ’Gun*’”) of me
–> {{aName:”Gundamo”, aVal:10}}

set cList to filterRecListByLabel(aRecList, “aVal >=80″) of me
–> {{aName:”piyoko”, aVal:100}, {aName:”piyomaru”, aVal:80}}

set dList to filterRecListByLabel(aRecList, “aVal=80 or aVal=10″) of me
–> {{aName:”piyomaru”, aVal:80}, {aName:”piyoo”, aVal:10}, {aName:”Gundam”, aVal:10}}

set eList to filterRecListByLabel(aRecList, “aVal=10 and aName like ’piyo*’”) of me
–> {{aName:”piyoo”, aVal:10}}

set fList to filterRecListByLabel(aRecList, “aName matches ’.*u$’”) of me –名前の最後が u で終わるものを、正規表現を用いて抽出
–> {{aName:”piyomaru”, aVal:80}}

set eList to filterRecListByLabel(aRecList, “aName == ’piyomaru’”) of me
–> {{aName:”piyomaru”, aVal:80}}

set eList to filterRecListByLabel(aRecList, “aName.length > 6″) of me –指定ラベルのデータの文字列長が6以上
–> {{aName:”piyomaru”, aVal:80}, {aName:”Gundamo”, aVal:10}}

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel(aRecList as list, aPredicate as string)
  –ListからNSArrayへの型変換
  
set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
  
–抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  
–NSArrayからListに型変換して返す
  
set bList to (filteredArray’s ASify()) as list
  
return bList
end filterRecListByLabel

★Click Here to Open This Script 

2012/06/13 AppleScript内でperl正規表現を使う

AppleScriptからperlを呼び出して正規表現によるテキスト検索、置換を行うサンプルです。

ごく近しい知り合いからもらったもので、他の人が作ったScriptをラッピングしてあります。

OSAXで正規表現の命令を追加したり、テキストエディタの正規表現の機能を呼び出したりするのもよいのですが、perlなどの他の言語処理系を呼び出した方がシンプルで済むこともあります。

本サンプルは「穴が開くほど使いまくったもの」(本人談)なので、安定度も折り紙付きです。

スクリプト名:AppleScript内でperl正規表現を使う.scpt
set aStr to “xxxx@ezweb.ne.jp”
set aResult to regex_match(“/ezweb/”, aStr) of doPerlKit
log {“aResult = “, aResult}

if aResult is true then
  –set aStr to regex_replace(”/e/A/”, aStr) of me
  
set aStr to regex_replace(“/e/A/g”, aStr) of doPerlKit –gオプションも使える
  
log {“aStr = “, aStr}
end if

——————————————
–AppleScript内でPerl正規表現を使用するためのルーチン群
——————————————
script doPerlKit
  
  
–★targetがregexにマッチするか判定
  
on regex_match(regex, target)
    set command to “$target = q/” & target & “/; if ($target =~ “ & regex & “){ print q/1/; }else{ print q/0/; }” –※引数を使用してperlコマンド生成
    
set str_result to exec_perl(command) of me
    
return evalResult(str_result) of me
  end regex_match
  
  
–★targetをregexで置換
  
on regex_replace(regex, target)
    set command to “$target = q/” & target & “/; $target =~ s” & regex & “; print $target;” –※引数を使用してperlコマンド生成
    
set str_result to exec_perl(command) of me
    
return evalResult(str_result) of me
    
–return str_result
  end regex_replace
  
  
–★commandをperlで実行
  
on exec_perl(command)
    set one_liner to “perl -e ‘” & command & “‘” as Unicode text
    
    
–エラー処理
    
try
      set str_result to do shell script one_liner –※shell実行
    on error
      set str_result to “syntax error : “ & one_liner –perlがエラーを吐いたとき
    end try
    
return str_result
  end exec_perl
  
  
–★結果がtrueかfalseかerrorか判断
  
on evalResult(str_result)
    if str_result is “1″ then
      return true
    else if str_result begins with “syntax error : “ then
      return str_result
    else
      return false
    end if
  end evalResult
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/05/06 ASObjC Runner〜ASOCの機能にAppleScript的な記法を

ASObjC Runnerは、ASOCのスクリプト(テキストベース)の実行機能をMac OS X 10.6.xと10.7.xに提供するフリーのアプリケーションです。作者はShane Stanley。ほかにも、Late Nighet SoftwareのMark Alldrittなども協力しているとのこと。

asobjc1.png

ASOCは基本的には、ランタイムアプリケーション上で動作するものですが、ASObjC Runnerではテキストファイル形式のASOCのプログラムをそのまま実行します。

tell application “ASObjC Runner”

のtellブロック内にscriptを記述します。サンプルはmacosxautomation.comに掲載されているので、雰囲気を伺い知ることができます。

asoctable.png

……以前までは、ASobjC Runnerはあまり個人的な興味を喚起されない存在だったのですが、途中のバージョンアップで面白い要素が追加されました。

Cocoaのオブジェクトに対して標準的なAppleScriptの用語でアクセスできるようにする機能です。

正確にいえば……ASObjC Runner側で想定している範囲の機能について、ASObjC RunnerがAppleScript用語辞書を用意しており、その用語を使って一般的なAppleScript的なアプローチでScriptingが行えるというわけです。

AS用語辞書が提供されており、通常のAppleScript的な用語が利用できるのは、

リスト操作(ソートや検索など)、文字列操作(検索、置換、日付文字列書式指定など。正規表現による検索/置換文字列指定の機能を含む)、ファイル操作(情報取得、コピー、削除、移動、ファイル作成、plist読み書き)などのほか、プログレスバー付きダイアログの表示

などです(ASOC的記法で書けば、より広い範囲のCocoaの機能をダイレクトに使用できます)。

2012/02/26 AppleScriptで正規表現(regexp)を

AppleScriptの基本文法に正規表現はありません。それでも、AppleScriptネイティブの機能で文字列加工などは普通にやっているわけですが、正規表現が使えないことに不満を感じているユーザーもいるようです。

でも、AppleScriptで正規表現が「使えない」なんて誰が決めたんでしょう? 標準命令セットに存在していないだけなのに。

(A)OSAXを追加
AppleScriptでは、命令などを追加するプラグイン機構「Scripting Additions」とか「OSAX」と呼ばれる仕組みがあります。

そもそも、標準命令自体が「Standard Additions」OSAXで提供されているほど。昔から正規表現を提供するOSAXは流通しており、使いたい人はインストールして使っているという状態。

ただし、どのユーザー環境にもインストールされているわけではないので、Scriptを配布して広く使ってもらうためには敷居が高いところです(自分専用のScriptであれば、問題はないでしょう)。

有名なところでは、仏Satimage Softwareの「Satimage OSAX」があります。

(B)正規表現を使えるアプリケーションを制御
正規表現を使えるアプリケーションをコントロールすれば、正規表現の使用は可能です。テキストエディタなど、対応しているものは多々あります。

ただ、これもすべてのユーザー環境にインストールされているわけではないので、Scriptを配布して広く使ってもらうという目的には合致していません。

(C)他の言語処理系を呼び出す
他の、正規表現の機能を持つ言語処理系をdo shell script命令で呼び出せば、使えることになります

一見、ゲテモノっぽい印象がありますが、どのユーザー環境にもPerlの処理系は入っていたりするので、どのユーザー環境でも使えるというメリットがあります。サブルーチン化しておくと、再利用性も高くなることでしょう。