Archive for the 'NSScanner' Category

2017/05/18 16進数文字列を10進数数値に変換するv2

16進数文字列をCocoaの機能を利用して10進数数値に変換するAppleScriptです。

以前、Pure AppleScriptで組んだものがありましたが、ためしにCocoaの機能を利用して実行速度を比較してみました。

 Pure AppleScript版:0.03秒ぐらい
 本AppleScript:0.02秒ぐらい

Cocoaの機能を呼び出すAppleScriptObjCのScriptは、Pure AppleScriptにくらべて1回目の実行がやや遅くなるものの、同じ処理を複数回呼び出すと2回目以降の速度が大幅に上がる傾向があります。繰り返し実行する場合にはAppleScriptObjC版のほうがおすすめです。

AppleScript名:16進数文字列を10進数数値に変換するv2
– Created 2017-05-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4645

set aHex to “0×9AC”
set hNum to retIntFromHexString(aHex) of me

on retIntFromHexString(aHex as string)
  set {aRes, hexRes} to (current application’s NSScanner’s scannerWithString:aHex)’s scanHexInt:(reference)
  
if aRes = false then
    return “” –エラーの場合
  else
    return hexRes
  end if
end retIntFromHexString

★Click Here to Open This Script 

2017/01/13 デコードしたQRコードのメールデータの各フィールドを取り出す v2.2

メールアドレス、Subject、Bodyの各フィールドが同梱されたQRコードをデコードした文字列を、docomo形式のフィールド形式と見立てて、各フィールドを取り出すAppleScriptです。

test_qr.png
–> “MATMSG:TO:hiyoko@piyocast.com;SUB:たいとる;BODY:ほんぶん;;”

こんな(↑)QRコードを読み取って、文字列にデコードできるわけですが、携帯電話用のQRコードはもともと、

名刺データ(氏名、住所など)交換、Webブックマーク(名前とURL)読み取り、メール文面生成などを目的として作られたため(フリー形式もありますけれども)、単にデータをデコードしただけではなく、これらのデータ形式であるかどうかのチェックが必要です。

これらのデータ形式だった場合には、それぞれフィールド値を取り出す必要があるわけですが、ここではメール文面の生成のみ対象にしています。

さて、各フィールドを解釈しようとしても、日本には主に3つの携帯電話キャリアがあり(docomo、au、softbank)、それぞれ微妙にこのフィールド定義形式が異なります。

調査したところ、docomo形式でないと読み取れないiOSアプリがあったため、docomo形式をデコードすることにします(AppStoreでも5本ぐらいのiOSアプリしかフィールド検出できませんでした)。海外産のiOSアプリでQRコードのこれらのデータ形式に対応しているケースではdocomo形式にのみ対応しているようでした(それでもあまたの未対応iOSアプリの1億倍えらい)。

先日、国内外のマニアさんによって寄ってたかって改良されまくった「&と=で区切られたテキストをrecordに(NSScanner版)」をもとに若干の手直しを行い、このためのサブルーチンに作り変えてみました。

Cocoaを呼び出すことで、AppleScript単体で(他のアプリケーションの力を借りずに)QRコードの作成や認識はできていますが、実際には携帯電話側でQRコードを読み取り、その内容をメールでMac側に転送し、Mac側で評価するような運用を想定しています。本Scriptはそのための「準備段階」のものです。

だいたいは、スマホでQRコードをスキャンして、その情報をメールで送ってくるので、Mac側で処理することを考えると、本文にデータが入ってくることだけを見ていればいいはずです。なので、スマホから来たデータ処理のためにはこんなたいそうなルーチンは必要ありません。

一応、Mac側でQRコードをデコードしたあとの「解釈」を行えるようにしておくことは無駄にはならないでしょう。

AppleScript名:デコードしたQRコードのメールデータの各フィールドを取り出す v2.2
– Created 2016-12-12 by Shane Stanley
– Modified 2016-12-14 by edama2
– Modified 2017-01-12 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4387

set aStr to “こんにちは、ぴよまるです

MATMSG:TO:hiyoko@piyocast.com;SUB:たいとる;BODY:ほんぶん;;

Takaaki Naganoya
XXX-XXXX-XXXX

iPhoneから送信”

set aDict to (parseStrByParamlabelAndTail(aStr, “MATMSG:”, “:”, “;”) of me)

set eMailAddrs to (aDict’s valueForKey:“TO”) as string
–>  ”hiyoko@piyocast.com”
set aSubject to (aDict’s valueForKey:“SUB”) as string
–>  ”たいとる”
set aBody to (aDict’s valueForKey:“BODY”) as string
–>  ”ほんぶん”

on parseStrByParamlabelAndTail(aParamStr, aDataHeader, aParamLabel, aParamTail)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
—Skip over the data header
  
set {theResult, theKey} to theScanner’s scanUpToString:aDataHeader intoString:(reference)
  
if theResult as boolean = false then return false –Error: Data header is not present
  
  
theScanner’s scanString:aDataHeader intoString:(missing value)
  
  
repeat until (theScanner’s isAtEnd as boolean)
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:aParamLabel intoString:(reference)
    
    
– skip over separator
    
theScanner’s scanString:aParamLabel intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:aParamTail intoString:(reference)
    
if theValue is missing value then set theValue to “”
    
    
– skip over separator
    
theScanner’s scanString:aParamTail intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
  end repeat
  
  
return aDict
end parseStrByParamlabelAndTail

★Click Here to Open This Script 

2016/12/14 &と=で区切られたテキストをrecordに 改

「aParam=1234&bParam=2345」のように、URLのパラメータ部のような文字列を「&」と「=」で区切ってrecordとして出力するAppleScriptです。

「&と=で区切られたテキストをrecordに(NSScanner版)」をedama2さんがさらに改良したものです。

「先日掲載されたNSScannerのスクリプト、自分でも試してみてとても便利だったんですが、値なしの時にエラーになるので修正してみました。

あとリファレンス本に「repeat until」はあんまり使わないと書いてあったので使ってみました。(edama2さん)」

なるほど。たしかにラベルだけで値が存在しないケースに対応できるとよさそうです。

AppleScript名:&と=で区切られたテキストをrecordに 改
– Created 2016-12-12 by Shane Stanley
– Modified 2016-12-14 by edama2
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4358

set aParamStr to "access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399&name="
set aDict to (parseStrByAmpAndEqual(aParamStr) of me)
–>  {expires:"86399", |name|:"", access_token:"XXxxx(XXxXXXXXxxXxxXXx))"}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:"=" intoString:(reference)
    
    
– skip over separator
    
theScanner’s scanString:"=" intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:"&" intoString:(reference)
    
if theValue is missing value then set theValue to "" –>追加
    
    
– skip over separator
    
theScanner’s scanString:"&" intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
  end repeat
  
  
return aDict as record
end parseStrByAmpAndEqual

★Click Here to Open This Script 

2016/12/12 &と=で区切られたテキストをrecordに(NSScanner版)

「aParam=1234&bParam=2345」のように、URLのパラメータ部のような文字列を「&」と「=」で区切ってrecordとして出力するAppleScriptです。

Shane Stanleyから「NSScannerを使った実装例」を教えてもらいました。「高速ではないけど、NSSCannerは便利なクラスなので慣れておいたほうがいいよ」という実例です。

Cocoaには、AppleScriptの処理系にはまったく存在していない強力な機能があり、使うととても便利で、プログラムを短く書くことができ、圧倒的に高速処理が可能です。

 リスト項目のユニーク化:NSSet
 リスト項目のユニーク化と出現回数カウント:NSCountedSet
 リスト項目のユニーク化とソート:NSMutableIndexSet
 リスト項目のしぼりこみ:NSPredicate

など、もはやプログラムを書く上で「ないと困る!」というレベルの道具になっています。ファイルパスの加工が強力なNSStringもないと困るレベルのものです。

NSScannerについて、Shaneのプログラムをそのまま掲載するとこんな感じですが、これは「巨大なプログラムを書き慣れた人が書く」タイプのプログラムであり、個別に理解を深めようとすると・・・つまり、「教材」として提示する際には若干の清書が必要になります。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner Orig)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
repeat
    set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
aDict’s setObject:theValue forKey:theKey
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

自分が動作内容を確認するためには、こんな感じに

(1)意味を取りやすいように空行を追加
(2)プログラムの動きを理解しやすいように、さまざまな変数のログ表示を行う
(3)プログラムのInputとOutputがわかりやすいように、サンプルデータの処理結果を掲載

のように書き換えます。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner 2)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record
–>  {expires:”86399″, access_token:”XXxxx(XXxXXXXXxxXxxXXx))”}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
log (theScanner’s scanLocation())
  
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
repeat
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
log (theScanner’s scanLocation())
    
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
log (theScanner’s scanLocation())
    
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
    
log (theScanner’s scanLocation())
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

そして、最終的に確認用のコードを削除して(クリーニングして)、こういうリストを掲載しています。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner 1)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record
–>  {expires:”86399″, access_token:”XXxxx(XXxXXXXXxxXxxXXx))”}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
repeat
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

2015/12/06 現在地点の緯度経度情報を取得する v2

Cocoaの機能を用いて、Macの現在位置の緯度経度情報を取得するAppleScriptです。実行時にWiFiがオンになっている必要があります。

だんだん、ASOCのScriptも書きこなれてきて、「この場合にはこう書く」的な方法論が蓄積され、便利に書けるようになってきました。処理系の方もCocoaを用いた記述に最適化されてきているのでしょうか(OS X 10.10ではForegroundで実行することを要求されましたが、OS X 10.11ではとくに文句も言われないので、、、)。

AppleScript名:GetCurrentLocation_ElCapitan v4
– Created 2015-03-04 by Takaaki Naganoya, Shane Stanley
– Modified 2015-12-06 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “CoreLocation”

property locationManager : missing value
property curLatitude : 0
property curLongitude : 0

set {aLat, aLong} to getCurrentGeoLocation() of me

on getCurrentGeoLocation()
  set locationManager to current application’s CLLocationManager’s alloc()’s init()
  
  
set locE to locationManager’s locationServicesEnabled()
  
if (locE as boolean) = true then
    locationManager’s setDelegate:me
    
locationManager’s setDesiredAccuracy:(current application’s kCLLocationAccuracyNearestTenMeters)
    
locationManager’s setDistanceFilter:500
    
locationManager’s startUpdatingLocation()
  else
    return false –error in init
  end if
  
  
set hitF to false
  
repeat 3000 times
    if {curLatitude, curLongitude} is not equal to {0, 0} then
      set hitF to true
      
exit repeat
    end if
    
delay 0.01
  end repeat
  
  
if hitF = false then return false
  
return {curLatitude, curLongitude}
end getCurrentGeoLocation

on locationManager:manager didUpdateLocations:locations
  set location to (locations’s lastObject())
  
set eventDate to (location’s timestamp())
  
set howRecent to (eventDate’s timeIntervalSinceNow())
  
set howRecent to howRecent as real
  
set howRecent to absNum(howRecent)
  
  
if howRecent < 15.0 then
    set alt to location’s altitude –>(NSNumber) 46.356517791748
    
set aSpeed to location’s speed –>(NSNumber) -1.0
    
set aCourse to location’s course –North:0, East:90, South:180, West:270
    
–>  (NSNumber) -1.0
    
set theDescription to location’s |description|()
    
–> (NSString) “< +35.xxxxx,+139.xxxxxx> +/- 65.00m (speed -1.00 mps / course -1.00) @ 2015/03/04 8時56分41秒 日本標準時”
    
set anNSScanner to current application’s NSScanner’s scannerWithString:theDescription
    
anNSScanner’s setCharactersToBeSkipped:(current application’s NSCharacterSet’s characterSetWithCharactersInString:“< ,")
    
set {theResult, aLat} to anNSScanner’s scanDouble:(reference)
    
set {theResult, aLng} to anNSScanner’s scanDouble:(reference)
    
copy {aLat, aLng} to {my curLatitude, my curLongitude}
  else
    locationManager’s stopUpdatingLocation()
  end if
  
end locationManager:didUpdateLocations:

on locationManager:anCLLocationManager didFailWithError:anNSError
  display dialog (anNSError’s localizedDescription() as text)
end locationManager:didFailWithError:

on absNum(aNum)
  if aNum > 0 then
    return aNum
  else
    return (aNum * -1)
  end if
end absNum

★Click Here to Open This Script 

2015/12/04 AlphabetとNumericの混在かどうかを調べる

Cocoaの機能を用いて、アルファベットと数字の混在状態になっているかどうかを調べるAppleScriptです。

AppleScript名:AlphabetとNumericの混在かどうかを調べる
– Created 2015-12-04 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aRes to chkMixtureOfNumericAndAlphabet(“ABC”) of me
–>  false

set aRes to chkMixtureOfNumericAndAlphabet(“123″) of me
–>  false

set aRes to chkMixtureOfNumericAndAlphabet(“4f73vg1v”) of me –Target
–>  true

set aRes to chkMixtureOfNumericAndAlphabet(“4f73vg1vあああ”) of me
–>  false

–数字とアルファベットの混在状態の時にtrueを返す
on chkMixtureOfNumericAndAlphabet(checkString)
  set a0Res to chkAlphabetAndNumeric(checkString) of me
  
set a1Res to chkNumeric(checkString) of me
  
set a2Res to chkAlphabet(checkString) of me
  
  
if {a0Res, a1Res, a2Res} = {true, false, false} then
    return true
  else
    return false
  end if
  
end chkMixtureOfNumericAndAlphabet

–数字のみかを調べて返す
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 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 chkAlphabetAndNumeric(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 “0″, 10))
  
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 chkAlphabetAndNumeric

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/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/03/12 CSVファイルをListにParseする(ASOC) 2

Shane Stanley rewrote me a true contents of his ASObjCExtras.framework’s “arrayFromCSV: commaIs:” method. Original version is written in Objective-C. He translated it into AppleScriptObjC to be understood for scripters.

There was no comment in his ASOC program and no blank line. So, I added comments and blanks to make it easy to read.

The script is very appropriate and tidy. To parse CSV file considering double-quote escape and irregular comma, there is need to use some flags to detect the state. The script have to scan CSV file by a character.

—-Japanese

ShaneがASObjCExtras.frameworkを用いてシンプルに書いたCSV parser scriptの「中身」(arrayFromCSV: commanIs:メソッド)をObjective-CからAppleScriptObjCに書き直して送ってくれました(理解しやすいように)。

ShaneのScriptにはコメントも空行も何もなかったので、解析ついでにいろいろコメントを足したり読みやすいように空行を追加しました。

Scriptの内容はきわめてまっとうで丁寧です。CSVをparseするには、ダブルクォート文字とかイレギュラーに行末に入るカンマがあったりとか、そうしたものを処理するためにいくつかのフラグを用いて根気よく処理する必要があります。ScriptはCSVを先頭から1文字づつ調べていく必要があります(もうちょっとは楽をしているかも)。

AppleScript名:CSVのParse 5(ASOC)
–Created By Shane Stanley 2015/03/12
–Commented & Arranged By Takaaki Naganoya 2015/03/12

use scripting additions
use framework “Foundation”

set theString to “cust1,\”prod,1\”,season 1,
cust1,prod1,season2,
cust2,prod1,event1,season1
cust2,prod3,event1,season 1″

its makeListsFromCSV:theString commaIs:“,”
–>  {​​​​​{​​​​​​​”cust1″, ​​​​​​​”prod,1″, ​​​​​​​”season 1″​​​​​}, ​​​​​{​​​​​​​”cust1″, ​​​​​​​”prod1″, ​​​​​​​”season2″​​​​​}, ​​​​​{​​​​​​​”cust2″, ​​​​​​​”prod1″, ​​​​​​​”event1″, ​​​​​​​”season1″​​​​​}, ​​​​​{​​​​​​​”cust2″, ​​​​​​​”prod3″, ​​​​​​​”event1″, ​​​​​​​”season 1″​​​​​}​​​}

–CSV Parser ASOC ver (Translated from “ASObjCExtras.framework” Objective-C version)
on makeListsFromCSV:theString commaIs:theComma
  
  
set theRows to {} –最終的に出力するデータ(2D Listになる)
  
  
set newLineCharSet to current application’s NSCharacterSet’s newlineCharacterSet() –改行キャラクタ
  
set importantCharSet to current application’s NSMutableCharacterSet’s characterSetWithCharactersInString:(“\”" & theComma) –カンマ
  
  
importantCharSet’s formUnionWithCharacterSet:newLineCharSet
  
  
set theNSScanner to current application’s NSScanner’s scannerWithString:theString
  
theNSScanner’s setCharactersToBeSkipped:(missing value)
  
  
  
–データ末尾を検出するまでループ
  
repeat while (theNSScanner’s isAtEnd() as integer = 0)
    
    
set insideQuotes to false
    
set finishedRow to false
    
set theColumns to {}
    
set currentColumn to “”
    
    
–すべての行を処理終了するまでループ(行内部の処理)
    
repeat while (not finishedRow)
      
      
set {theResult, tempString} to theNSScanner’s scanUpToCharactersFromSet:importantCharSet intoString:(reference)
      
–log {”theResult”, theResult, “tempString”, tempString}
      
      
if theResult as integer = 1 then set currentColumn to currentColumn & (tempString as text)
      
–log {”currentColumn”, currentColumn}
      
      
–データ末尾検出
      
if theNSScanner’s isAtEnd() as integer = 1 then
        if currentColumn is not “” then set end of theColumns to currentColumn
        
set finishedRow to true
        
      else
        –データ末尾ではない場合
        
set {theResult, tempString} to theNSScanner’s scanCharactersFromSet:newLineCharSet intoString:(reference)
        
        
if theResult as integer = 1 then
          
          
if insideQuotes then
            –ダブルクォート文字内の場合
            
set currentColumn to currentColumn & (tempString as text)
          else
            –ダブルクォート内ではない場合
            
if currentColumn is not “” then set end of theColumns to currentColumn
            
set finishedRow to true
          end if
          
        else
          –行末文字が見つからない場合
          
set theResult to theNSScanner’s scanString:“\”" intoString:(missing value)
          
          
if theResult as integer = 1 then
            –ダブルクォート文字が見つかった場合
            
if insideQuotes then
              –ダブルクォート文字内の場合
              
set theResult to theNSScanner’s scanString:“\”" intoString:(missing value)
              
              
if theResult as integer = 1 then
                set currentColumn to currentColumn & “\”"
              else
                set insideQuotes to (not insideQuotes)
              end if
            else
              –ダブルクォート文字内ではない場合
              
set insideQuotes to (not insideQuotes)
            end if
            
          else
            –ダブルクォート文字が見つからなかった場合
            
set theResult to theNSScanner’s scanString:theComma intoString:(missing value) –カンマの検索
            
            
if theResult as integer = 1 then
              if insideQuotes then
                set currentColumn to currentColumn & theComma
              else
                set end of theColumns to currentColumn
                
set currentColumn to “”
                
theNSScanner’s scanCharactersFromSet:(current application’s NSCharacterSet’s whitespaceCharacterSet()) intoString:(missing value)
              end if
            end if
          end if
        end if
      end if
      
    end repeat
    
    
if (count of theColumns) > 0 then set end of theRows to theColumns –行データ(1D List)をtheRowsに追加(2D List)
    
  end repeat
  
  
return theRows
  
end makeListsFromCSV:commaIs:

★Click Here to Open This Script 

2015/03/04 現在地点の緯度経度情報を取得する

This AppleScript get current location of your Mac by using CoreLocation. There is need to enable WiFi to get location using this AppleScript.

You can get altitude, timestamp, speed and course easily. But latitude and longitude were difficult to get.

I asked to Shane how to do this. His answer is “get description”.

You must execute this AppleScript in foreground. Original Script is made for a part of ASOC project on Xcode.

loc1.png
▲システム環境設定の「セキュリティとプライバシー」でScript EditorもしくはASObjC Explorer 4に位置情報の取得を許可しておく必要があります

loc2.png
▲許可しないとエラーが表示されます

本AppleScriptはお使いのMacの現在位置の緯度経度情報を取得するものです。CoreLocationを用いて、高度、タイムスタンプ、スピード、進行方向については容易に取得できました(位置情報を取得するためにWiFiをオンにしておく必要あり)。ただ、肝心の緯度経度情報だけはなかなか取得できず、Shaneに聞いてみたところ・・・「descriptionを取得しろ」とのこと(サンプルつきで)。

本AppleScriptの実行時には、foregroundで明示的に実行させる必要があります。オリジナルのScriptはASOC on Xcodeの一部として書いたもので、位置情報の取得/許可については本Scriptの方がうまくいっていない感じです(ASObjC Explorerがコケたりする)。

AppleScript名:GetCurrentLocation_Yosemite v3
– Created 2015-03-04 by Takaaki Naganoya, Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “CoreLocation”
use framework “AppKit”

property curLatitude : 0
property curLongitude : 0

on run
  startStandardUpdates()
end run

on startStandardUpdates()
  –Listing 1-1 Starting the standard location service
  
set locationManager to current application’s CLLocationManager’s alloc()’s init()
  
  
set locE to locationManager’s locationServicesEnabled()
  
if (locE as boolean) = true then
    locationManager’s setDelegate:me
    
locationManager’s setDesiredAccuracy:(current application’s kCLLocationAccuracyNearestTenMeters)
    
locationManager’s setDistanceFilter:500
    
locationManager’s startUpdatingLocation()
  end if
  
end startStandardUpdates

on locationManager:manager didUpdateLocations:locations
  
  
–Listing 1-3 Processing an incoming location event
  
set location to (locations’s lastObject())
  
set eventDate to (location’s timestamp())
  
set howRecent to (eventDate’s timeIntervalSinceNow())
  
  
–Calc ABS
  
set howRecent to howRecent as real
  
set howRecent to absNum(howRecent)
  
  
if howRecent < 15.0 then
    
    
set alt to location’s altitude
    
–>  (NSNumber) 46.356517791748
    
    
set aSpeed to location’s speed
    
–>  (NSNumber) -1.0
    
    
set aCourse to location’s course –North:0, East:90, South:180, West:270
    
–>  (NSNumber) -1.0
    
    
–By Shane Stanley
    
set theDescription to location’s |description|()
    
–> (NSString) “< +35.xxxxx,+139.xxxxxx> +/- 65.00m (speed -1.00 mps / course -1.00) @ 2015/03/04 8時56分41秒 日本標準時”
    
    
set anNSScanner to current application’s NSScanner’s scannerWithString:theDescription
    
anNSScanner’s setCharactersToBeSkipped:(current application’s NSCharacterSet’s characterSetWithCharactersInString:“< ,")
    
set {theResult, aLat} to anNSScanner’s scanDouble:(reference)
    
set {theResult, aLng} to anNSScanner’s scanDouble:(reference)
    
    
copy {aLat, aLng} to {my curLatitude, my curLongitude}
    
  else
    stopUpdatingLocation()
  end if
  
end locationManager:didUpdateLocations:

on locationManager:anCLLocationManager didFailWithError:anNSError
  display dialog (anNSError’s localizedDescription() as text)
end locationManager:didFailWithError:

on absNum(aNum)
  if aNum > 0 then
    return aNum
  else
    return (aNum * -1)
  end if
end absNum

★Click Here to Open This Script