Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

タグ: NSScanner

よみがなタグを削除

Posted on 7月 1, 2020 by Takaaki Naganoya

与えられたテキスト(おそらくHTML)から、指定のタグで囲まれた要素を削除するAppleScriptです。

Web上の表をCSVとして書き出すAppleScriptを動かしていて、Wikipediaの表を処理したときに「よみがなタグ」がそのまま文字列として展開されることに気づきました。

その場で既存のルーチンを使いまわして、よみがなタグの削除機能を追加。その追加内容です。


▲上は、Wikipedia掲載の表をそのままCSV化したもの。下は、本Scriptを追加してルビタグを削除するように改良したScriptの処理結果

AppleScript名:指定タグを削除.scpt
— Created 2016-12-12 by Shane Stanley
— Modified 2016-12-14 by edama2
— Modified 2017-11-28 by Takaaki Naganoya
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aStr to getData() of me
set aRes to (trimStrFromTo(aStr, "<span style=\"display:none;speak:none\">", "</span>") of me)
–>  
(*
"
<td><b><a href=\"/wiki/aaaaa" title=\"こちら葛飾区亀有公園前派出所\">こちら葛飾区<br /> 亀有公園前派出所</a></b></td>
<td><a href=\"/wiki/%E7%A7%8B%E6%9C%AC%E6%B2%BB\" title=\"秋本治\">秋本治</a></td>
"
*)

on trimStrFromTo(aParamStr, fromStr, toStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set anArray to current application’s NSMutableArray’s array()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {theResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
    
theScanner’s scanString:fromStr intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
if theValue is missing value then set theValue to ""
    
    
theScanner’s scanString:toStr intoString:(missing value)
    
    
anArray’s addObject:theValue
  end repeat
  
  
if anArray’s |count|() = 0 then return aParamStr
  
  
copy aParamStr to curStr
  
repeat with i in (anArray as list)
    set curStr to repChar(curStr, fromStr & i & toStr, "") of me
  end repeat
  
  
return curStr
end trimStrFromTo

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 as string
  
return cString
end repChar

on getData()
  return "
<td><span style=\"display:none;speak:none\">こちらかつしかくかめありこうえんまえはしゆつしよ/</span><b><a href=\"/wiki/aaaaa\" title=\"こちら葛飾区亀有公園前派出所\">こちら葛飾区<br /> 亀有公園前派出所</a></b></td>
<td><span style=\"display:none;speak:none\">あきもと おさむ/</span><a href=\"/wiki/%E7%A7%8B%E6%9C%AC%E6%B2%BB\" title=\"秋本治\">秋本治</a></td>
"

end getData

★Click Here to Open This Script 

Posted in Text | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy NSMutableArray NSScanner NSString | Leave a comment

指定のドメインのIPアドレスを指定DNSで引いて返す

Posted on 5月 26, 2020 by Takaaki Naganoya

指定のドメインのIPアドレスを、指定のDNSサーバーで引いて結果を返すAppleScriptです。

DNS系のFramework(Objective-Cで書いてあるもの)はいろいろためしてみたものの、不思議とどれも動作が重くて、このようにshell commandを呼び出したほうが手軽で高速でした。

# うまく動いていなかったために遅かった、というのが正確なところかも。タイムアウトエラーを起こして処理が完了しないものばかりだったので、、、書き換えがうまく行っていなかった可能性が50%以上あります

nslookupの結果を取り出すためにいろいろやっていますが、ありもののルーチンを引っ張り出して加工してしているだけです。

apple.comのように、CDNサービスを利用してアクセス負荷分散を行なっているようなドメインだと、問い合わせごとにIPアドレスが変わるようです。

一方で、piyocast.comのように固定ホスト(バーチャルホストではありますが)で細々とホスティングしているようなドメインでは、IPアドレスが毎回変わるということはありません。

AppleScript名:指定のドメインのIPアドレスを指定DNSで引いて返す v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/05/25
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property NSScanner : a reference to current application’s NSScanner
property NSMutableArray : a reference to current application’s NSMutableArray

set aDomain to "apple.com"
set aDNS to "8.8.4.4"
set dnRes to getIPAddress(aDomain, aDNS) of me
–> {serverName:"apple.com", serverAddr:{"17.172.224.47", "17.178.96.59", "17.142.160.59"}}–CDNの影響でアドレスが頻繁に変わる?

set dnRes to getIPAddress("piyocast.com", aDNS) of me
–> {serverName:"piyocast.com", serverAddr:{"157.112.183.74"}}

on getIPAddress(aDomain, aDNS)
  set searchStr to "answer:"
  
  
using terms from scripting additions
    set aRes to do shell script ("nslookup " & aDomain & " " & aDNS)
    
set bRes to offset of searchStr in aRes
  end using terms from
  
  
if bRes = 0 then return false
  
  
set cRes to text (bRes + (length of searchStr) + 1) thru -1 of aRes
  
  
set adRes1 to extractStrFromTo(cRes, "Name:", return) of me
  
set adRes2 to extractStrFromTo(cRes, "Address:", return) of me
  
  
set outList to {}
  
set erList to {}
  
repeat with i from 1 to (length of adRes2)
    set j1 to contents of (item i of adRes1)
    
set j2 to contents of (item i of adRes2)
    
if j1 is equal to aDomain then
      set the end of outList to j2
    else
      set the end of erList to {j1, j2}
    end if
  end repeat
  
  
return {serverName:j1, serverAddr:outList}
end getIPAddress

–offset命令の実行を横取りして高速実行(x2.5 faster)
on offset of searchStr in str
  set aRes to getOffset(str, searchStr) of me
  
return aRes
end offset

on getOffset(str, searchStr)
  set d to divideBy(str, searchStr)
  
if (count d) is less than 2 then return 0
  
return (length of item 1 of d) + 1
end getOffset

on divideBy(str, separator)
  set delSave to AppleScript’s text item delimiters
  
set the AppleScript’s text item delimiters to separator
  
set strItems to every text item of str
  
set the AppleScript’s text item delimiters to delSave
  
return strItems
end divideBy

–指定文字と終了文字に囲まれた内容を抽出
on extractStrFromTo(aParamStr, fromStr, toStr)
  set theScanner to NSScanner’s scannerWithString:aParamStr
  
set anArray to NSMutableArray’s array()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {theResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
theScanner’s scanString:fromStr intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
if theValue is missing value then set theValue to "" –>追加
    
theScanner’s scanString:toStr intoString:(missing value)
    
anArray’s addObject:theValue
  end repeat
  
  
return (anArray as list)
end extractStrFromTo

★Click Here to Open This Script 

Posted in Internet | Tagged 10.13savvy 10.14savvy 10.15savvy NSMutableArray NSScanner | Leave a comment

Previewで現在表示中のPDFのページ番号を抽出する

Posted on 4月 29, 2020 by Takaaki Naganoya

macOS標準搭載の画像/PDFビューワーの「Preview.app」で表示中のPDFの、現在表示中のページの番号を取得するAppleScriptです。

本来、Preview.appのような超低機能アプリケーションから強引に情報を(GUI Scriptingまで使って)取得する意味はありません。PDFビューワーとしてまっとうな機能を持っているSkimを使って表示中のページ番号を取得するのが筋です。

ただ、どうしてもPreviewでないといけないケースで、仕方なく作ったものですが、英語環境でも日本語環境でも同様に動くために作ってみたらこんな感じに。指定アプリケーション単体で言語環境を指定して起動できると、各言語環境における動作確認が手軽に行えてよいと思うものの、手段がありそうで見つかりません(Xcode上でそういう起動ができるので、不可能ではないと思うのですが)。


▲英語環境で実行したところ(macOS 10.14.6)


▲日本語環境で実行したところ(macOS 10.15.4)

仕方なくGUI Scripting経由でウィンドウのタイトルを取得して、ファイル名とページ情報を文字列処理で分離しています。このあたり、英語環境と日本語環境でセパレータ(括弧)が異なるので、分離したページ情報から数字判定を行なって取得しています。Preview.appのアプリケーションバンドル内にこうしたフォーマットのテキストが存在していれば、そちらを使うべきです(見つかっていないので現状こんな感じで)。

GUI Scripting内でプロセス名を指定する箇所で、ローカライズされたプロセス名をCocoaの機能を用いて取得しています。これで、英語環境と日本語環境については問題なく共通Scriptでカバーできています。

AppleScript名:Previewで現在表示中のPDFのページ番号を抽出する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/04/29
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set pNum to retPreviewDispPageNumber() of me

on retPreviewDispPageNumber()
  set aLocName to getLocalizedName("com.apple.Preview") of me
  
  
tell application "Preview"
    if (count every document) = 0 then return false
  end tell
  
  
tell application "System Events"
    tell process aLocName
      tell window 1
        set aTitle to title
      end tell
    end tell
  end tell
  
  
set aPageInfo to pickUpFromToStr(aTitle, "(", ")") of me –English env
  
if aPageInfo = false then
    set aPageInfo to pickUpFromToStr(aTitle, "(", ")") of me –double witdh parenthesis (Japanese env)
  end if
  
set pList to words of aPageInfo
  
set hitF to false
  
repeat with i in pList
    set nRes to chkNumeric(i) of me
    
if nRes = true then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then return
  
return i as integer
end retPreviewDispPageNumber

on getLocalizedName(aBundleID as string)
  set pRes to getProcessByBUndleID(aBundleID) of me
  
if pRes = false then return ""
  
set pName to pRes’s localizedName()
  
return pName as string
end getLocalizedName

–指定プロセスを取得する
on getProcessByBUndleID(aBundleID)
  set appArray to current application’s NSRunningApplication’s runningApplicationsWithBundleIdentifier:aBundleID
  
if appArray’s |count|() > 0 then
    set appItem to appArray’s objectAtIndex:0
    
return appItem
  else
    return false
  end if
end getProcessByBUndleID

on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
  set a1Offset to offset of s1Str in aStr
  
if a1Offset = 0 then return false
  
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
  
  
set a2Offset to offset of s2Str in bStr
  
if a2Offset = 0 then return false
  
  
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
  
  
return cStr as string
end pickUpFromToStr

–数字のみかチェック
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 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 

Posted in GUI Scripting PDF Text | Tagged 10.13savvy 10.14savvy 10.15savvy NSCharacterSet NSRunningApplication NSScanner Preview | Leave a comment

iTunes Libraryの再生回数をジャンルごとに集計、ジャンル名名寄せ付き

Posted on 12月 2, 2019 by Takaaki Naganoya

iTunesLibrary.framework経由でiTunes/Music.appのライブラリのジャンルごとの再生回数を集計するAppleScriptです。

7,000曲程度入っているライブラリで、ジャンル名の名寄せも含め、開発環境(MacBook Pro Retina 2012, Core i7 2.66GHz@macOS 10.14.6)で0.7秒程度で終了します。2,500曲程度入っているMac mini 2014 Core i5 2.6GHz@macOS 10.15.1で0.6秒程度です。

macOS 10.14まで(iTunes.app)と、macOS 10.15以降(Music.app)でも同様にiTunesLibrary.framework経由でライブラリへのアクセスが行えます。

AppleScript名:iTunes Libraryの再生回数をジャンルごとに集計、ジャンル名名寄せ付き
— Created 2019-11-10 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "iTunesLibrary"
–https://developer.apple.com/reference/ituneslibrary/itlibmediaitem?language=objc

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSScanner : a reference to current application’s NSScanner
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSMutableCharacterSet : a reference to current application’s NSMutableCharacterSet

–ジャンル名寄せリスト(2要素から構成される2D List)
set genreNayoseList to {{"World", "ワールド"}, {"Anime", "アニメ"}, {"Electronic", "エレクトロニック"}, {"R&B/ソウル", "R&B/ソウル"}, {"Kayokyoku", "歌謡曲"}, {"Electronic", "エレクトロニック"}, {"Vocal", "ヴォーカル"}, {"Classical", "クラシック"}, {"Dance", "ダンス"}, {"Soundtrack", "サウンドトラック"}, {"Rock", "ロック"}}

set library to current application’s ITLibrary’s libraryWithAPIVersion:"1.0" |error|:(missing value)
if library is equal to missing value then return

set aRes1 to (library’s applicationVersion()) as string –>  "12.10.1.37" @ macOS 10.15
set aRes2 to (library’s apiMinorVersion()) –> 1
set aRes3 to (library’s apiMajorVersion()) –> 1

set playLists to library’s allPlaylists()
set gArray to library’s allMediaItems()’s genre
set aRes to countItemsByItsAppearance(gArray) of me

set bRes to genreNayoseAndUnify(aRes, genreNayoseList) of me
–> {{genreName:"サウンドトラック", numberOfTimes:1965}, {numberOfTimes:1209, genreName:"Podcast"}, {genreName:"ロック", numberOfTimes:1128}, {genreName:"クラシック", numberOfTimes:705}, {numberOfTimes:517, genreName:"ポップ"}, {genreName:"アニメ", numberOfTimes:533}, {numberOfTimes:383, genreName:"J-Pop"}, {numberOfTimes:292, genreName:"Pop"}, {numberOfTimes:279, genreName:"社会/文化"}, {numberOfTimes:252, genreName:missing value}, {genreName:"ワールド", numberOfTimes:246}, {numberOfTimes:187, genreName:"ジャズ"}, {genreName:"エレクトロニック", numberOfTimes:168}, {numberOfTimes:125, genreName:"R&B"}, {numberOfTimes:104, genreName:"ニューエイジ"}, {numberOfTimes:81, genreName:"Unclassifiable"}, {genreName:"歌謡曲", numberOfTimes:60}, {numberOfTimes:57, genreName:"Children’s"}, {numberOfTimes:54, genreName:"オルタナティブ"}, {numberOfTimes:38, genreName:"Holiday"}, {numberOfTimes:32, genreName:"Data"}, {numberOfTimes:31, genreName:"イージーリスニング"}, {genreName:"ヴォーカル", numberOfTimes:31}, {numberOfTimes:19, genreName:"iTunes U"}, {numberOfTimes:17, genreName:"フォーク"}, {numberOfTimes:15, genreName:"ブルース"}, {numberOfTimes:15, genreName:"ディズニー"}, {numberOfTimes:15, genreName:"シンガーソングライター"}, {numberOfTimes:14, genreName:"Easy Listening"}, {numberOfTimes:14, genreName:"ラテン"}, {numberOfTimes:14, genreName:"Electronica/Dance"}, {numberOfTimes:14, genreName:"個人ジャーナル"}, {genreName:"ダンス", numberOfTimes:12}, {numberOfTimes:10, genreName:"アクション/アドベンチャー"}, {numberOfTimes:9, genreName:"J-POP"}, {numberOfTimes:9, genreName:"New Age"}, {numberOfTimes:7, genreName:"演歌"}, {numberOfTimes:6, genreName:"少年"}, {numberOfTimes:6, genreName:"青年"}, {numberOfTimes:6, genreName:"キッズ/ファミリー"}, {numberOfTimes:5, genreName:"Video"}, {numberOfTimes:5, genreName:"プログラミング"}, {numberOfTimes:4, genreName:"ホリデー"}, {numberOfTimes:4, genreName:"カントリー"}, {numberOfTimes:4, genreName:"科学/医学"}, {numberOfTimes:3, genreName:"ビジネス"}, {numberOfTimes:3, genreName:"コメディ"}, {numberOfTimes:3, genreName:"Game Music"}, {numberOfTimes:3, genreName:"Latin"}, {genreName:"R&B/ソウル", numberOfTimes:5}, {numberOfTimes:2, genreName:"#NIPPONSEI @ IRC.MIRCX.COM"}, {numberOfTimes:2, genreName:"Technology"}, {numberOfTimes:2, genreName:"ヒップホップ/ ラップ"}, {numberOfTimes:2, genreName:"ヒップホップ/ラップ"}, {numberOfTimes:2, genreName:"日本"}, {numberOfTimes:2, genreName:"ドラマ"}, {numberOfTimes:1, genreName:"社会科学"}, {numberOfTimes:1, genreName:"コンピュータ/テクノロジー"}, {numberOfTimes:1, genreName:"Tech ニュース"}, {numberOfTimes:1, genreName:"科学/自然"}, {numberOfTimes:1, genreName:"その他"}, {numberOfTimes:1, genreName:"児童書フィクション"}, {numberOfTimes:1, genreName:"レゲエ"}, {numberOfTimes:1, genreName:"Lifestyle & Home"}, {numberOfTimes:1, genreName:"ホリデーミュージック"}, {numberOfTimes:1, genreName:"マネジメント/リーダーシップ"}, {numberOfTimes:1, genreName:"インストゥルメンタル"}, {numberOfTimes:1, genreName:"SF/ファンタジー"}, {numberOfTimes:1, genreName:"146"}, {numberOfTimes:1, genreName:"健康/フィットネス"}, {numberOfTimes:1, genreName:"148"}, {numberOfTimes:1, genreName:"NHK FM(東京)"}, {numberOfTimes:1, genreName:"Seattle Pacific University – Latin"}, {numberOfTimes:1, genreName:"チルドレン・ミュージック"}, {numberOfTimes:1, genreName:"名作"}, {numberOfTimes:1, genreName:"Folk"}}

–ジャンルのリストを出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{"genreName", "numberOfTimes"})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to NSSortDescriptor’s sortDescriptorWithKey:"numberOfTimes" ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

on genreNayoseAndUnify(aList as list, genreNayoseList as list)
  set gList to FlattenList(genreNayoseList) of me
  
  
set didProc to {}
  
  
set a2List to {}
  
  
repeat with i in aList
    set aGenre to genreName of i
    
    
if (aGenre is in gList) and (aGenre is not in didProc) then
      
      
repeat with ii in genreNayoseList
        set jj to contents of ii
        
        
if aGenre is in jj then
          copy jj to {g1, g2}
          
          
          
if chkAlphabet(g1) of me = true then
            set targG to g2
            
set targG2 to g1
          else
            set targG to g1
            
set targG2 to g2
          end if
          
          
          
set s1Res to searchByGenreName(aList, targG) of me
          
set s2Res to searchByGenreName(aList, targG2) of me
          
          
          
set s3Res to addMutipleLists({s1Res, s2Res}) of me
          
          
set tmpClass to class of s3Res
          
if tmpClass = list then
            set s3Res to contents of first item of s3Res
          end if
          
          
set outRec to {genreName:targG, numberOfTimes:s3Res}
          
set the end of didProc to g1
          
set the end of didProc to g2
          
          
exit repeat
        end if
        
      end repeat
      
      
set the end of a2List to outRec
    else
      if (aGenre is not in didProc) then
        set the end of a2List to contents of i
      end if
    end if
  end repeat
  
  
return a2List
end genreNayoseAndUnify

on searchByGenreName(aList as list, aGenreName as string)
  set predicatesStr to "genreName == ’" & aGenreName & "’"
  
set anArray to (NSArray’s arrayWithArray:aList)
  
set aPred to (NSPredicate’s predicateWithFormat:predicatesStr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred)
  
if (bRes as list) = {} then return {}
  
  
set bbRes to first item of bRes
  
return (numberOfTimes of bbRes) as list
end searchByGenreName

on addMutipleLists(s1List)
  script spdAdd
    property s1List : {}
    
property s3List : {}
  end script
  
  
copy s1List to (s1List of spdAdd) –init
  
  
set s1Len to length of first item of (s1List of spdAdd)
  
set (s3List of spdAdd) to makeZero1DList(s1Len, 0) of me
  
  
repeat with i in (s1List of spdAdd)
    set tmpLen to length of i
    
if tmpLen is not equal to s1Len then return false
    
    
repeat with ii from 1 to s1Len
      set tmp1 to contents of item ii of (s3List of spdAdd)
      
set tmp2 to contents of item ii of i
      
      
set item ii of (s3List of spdAdd) to (tmp1 + tmp2)
    end repeat
  end repeat
  
  
return (s3List of spdAdd)
end addMutipleLists

–指定要素を指定回数追加したリストを作成する
on makeZero1DList(itemMax, itemElem)
  set allData to {}
  
repeat itemMax times
    set the end of allData to itemElem
  end repeat
  
return allData
end makeZero1DList

–By Paul Berkowitz
–2009年1月27日 2:24:08:JST
–Re: Flattening Nested Lists
on FlattenList(aList)
  set oldDelims to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {"????"}
  
set aString to aList as text
  
set aList to text items of aString
  
set AppleScript’s text item delimiters to oldDelims
  
return aList
end FlattenList

— アルファベットのみか調べて返す
on chkAlphabet(checkString)
  set aStr to NSString’s stringWithString:checkString
  
set allCharSet to 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 chkCompareString:checkString baseString:baseString
  set aScanner to 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 

Posted in list | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSArray NSCountedSet NSDictionary NSMutableArray NSMutableCharacterSet NSPredicate NSScanner NSSortDescriptor NSString | Leave a comment

ジャンル名名寄せ v2

Posted on 11月 27, 2019 by Takaaki Naganoya

iTunes/Musicのライブラリに入っている楽曲データのジャンル分けのゆらぎ吸収を行う、ジャンル名のいわゆる「名寄せ」を行うAppleScriptです。


▲1つのアルバム中でもジャンル名に表記ゆれがあるのは勘弁してほしい

iTunes/Musicライブラリ中の全楽曲の最終再生日時を24時間の各時で集計を行い、ジャンルごとに再生時間に偏りがないかどうかを分析するため、さらにジャンルごろに集計してみました。

「名寄せ」(なよせ)は昔から情報処理に見られる泥臭い地道な処理で、データ上の表記ゆれを吸収するための処理です。地名などの固有名詞で多く見られます(念のためにWikipediaで調べたら、異なる業界では割とブラックな言葉として用いられている模様)。

このジャンル名が英語で入っていたり日本語で入っていたりと、ゆらぎが発生しているので、同様のジャンル名であれば同じものとして合算して集計してみました。

また、英語で表記されたジャンル名ではなく日本語で表記されたものを採用(=すべてアルファベットで書かれたものを不採用)しています。まだ、それほどいじめ抜いていない(自分の環境でしかテストしていない)ので、ジャンル名の名寄せリストはもう少し鍛えておく必要があるものと思っています。

AppleScript名:ジャンル名名寄せ v2
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/11/24
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5" — El Capitan (10.11) or later
use framework "Foundation"
use scripting additions

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSScanner : a reference to current application’s NSScanner
property NSPredicate : a reference to current application’s NSPredicate
property NSMutableCharacterSet : a reference to current application’s NSMutableCharacterSet

–ジャンル名寄せリスト(2要素から構成される2D List)
set genreNayoseList to {{"World", "ワールド"}, {"Anime", "アニメ"}, {"Electronic", "エレクトロニック"}, {"R&B/ソウル", "R&B/ソウル"}, {"Kayokyoku", "歌謡曲"}, {"Electronic", "エレクトロニック"}, {"Vocal", "ヴォーカル"}, {"Classical", "クラシック"}, {"Dance", "ダンス"}, {"Soundtrack", "サウンドトラック"}}

–名寄せ対象リスト(genreNameを名寄せ)
set aList to {{genreName:"シンガーソングライター", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 1, 5, 0, 0, 0, 0}}, {genreName:"ロック", playingList:{2, 2, 0, 0, 0, 1, 0, 0, 4, 3, 9, 5, 11, 11, 22, 19, 48, 17, 28, 15, 2, 11, 13, 1}}, {genreName:"Soundtrack", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, {genreName:"World", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, {genreName:"演歌", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ホリデーミュージック", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ディズニー", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0}}, {genreName:"J-Pop", playingList:{4, 0, 1, 1, 2, 1, 5, 3, 2, 3, 27, 19, 14, 26, 17, 20, 28, 32, 40, 25, 10, 10, 6, 6}}, {genreName:"ヒップホップ/ ラップ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"Electronic", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"アニメ", playingList:{7, 1, 0, 0, 7, 2, 0, 0, 0, 9, 23, 45, 17, 10, 17, 50, 48, 65, 53, 27, 12, 16, 23, 45}}, {genreName:"ポップ", playingList:{3, 0, 0, 0, 0, 0, 0, 3, 1, 6, 2, 9, 7, 0, 11, 7, 8, 10, 7, 5, 3, 1, 0, 1}}, {genreName:"Kayokyoku", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}}, {genreName:"ヒップホップ/ラップ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"Dance", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}}, {genreName:"ダンス", playingList:{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0}}, {genreName:"R&B/ソウル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"Anime", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0}}, {genreName:"チルドレン・ミュージック", playingList:{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"Classical", playingList:{1, 1, 0, 0, 2, 1, 8, 1, 0, 0, 11, 5, 2, 0, 4, 9, 13, 2, 2, 0, 0, 0, 0, 0}}, {genreName:"ニューエイジ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0}}, {genreName:"Pop", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, {genreName:"ヴォーカル", playingList:{0, 0, 0, 0, 0, 1, 0, 0, 0, 6, 7, 0, 0, 1, 6, 0, 0, 3, 1, 0, 3, 0, 0, 0}}, {genreName:"サウンドトラック", playingList:{23, 31, 0, 0, 0, 2, 1, 0, 7, 7, 27, 29, 26, 25, 28, 48, 66, 76, 56, 16, 8, 31, 6, 10}}, {genreName:"歌謡曲", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 2, 4, 5, 5, 4, 6, 6, 7, 1, 0, 0, 0}}, {genreName:"ホリデー", playingList:{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}}, {genreName:"Folk", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}}, {genreName:"ジャズ", playingList:{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 16, 7, 3, 9, 0, 5, 10, 20, 10, 2, 1, 0, 0}}, {genreName:"エレクトロニック", playingList:{0, 1, 3, 0, 0, 0, 0, 1, 4, 13, 8, 13, 2, 18, 6, 13, 10, 16, 25, 7, 8, 0, 2, 10}}, {genreName:"ワールド", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 5, 5, 7, 6, 0, 0, 0, 0, 0}}, {genreName:"インストゥルメンタル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}}, {genreName:"Vocal", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"クラシック", playingList:{0, 0, 1, 1, 0, 3, 2, 0, 4, 3, 13, 11, 24, 1, 21, 14, 24, 38, 21, 7, 5, 8, 4, 5}}, {genreName:"オルタナティブ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 0, 6, 9, 0}}, {genreName:"R&B/ソウル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}

set bList to genreNayoseAndUnify(aList, genreNayoseList) of me
–> {{genreName:"シンガーソングライター", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 1, 5, 0, 0, 0, 0}}, {genreName:"ロック", playingList:{2, 2, 0, 0, 0, 1, 0, 0, 4, 3, 9, 5, 11, 11, 22, 19, 48, 17, 28, 15, 2, 11, 13, 1}}, {genreName:"サウンドトラック", playingList:{23, 31, 0, 0, 0, 2, 1, 0, 7, 7, 28, 29, 26, 25, 28, 48, 66, 77, 56, 16, 8, 31, 6, 10}}, {genreName:"ワールド", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 5, 5, 8, 6, 0, 0, 0, 0, 0}}, {genreName:"演歌", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ホリデーミュージック", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ディズニー", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0}}, {genreName:"J-Pop", playingList:{4, 0, 1, 1, 2, 1, 5, 3, 2, 3, 27, 19, 14, 26, 17, 20, 28, 32, 40, 25, 10, 10, 6, 6}}, {genreName:"ヒップホップ/ ラップ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"エレクトロニック", playingList:{0, 1, 3, 0, 0, 0, 0, 1, 4, 13, 8, 13, 2, 19, 6, 13, 10, 16, 25, 7, 8, 0, 2, 10}}, {genreName:"アニメ", playingList:{7, 1, 0, 0, 7, 2, 0, 0, 0, 9, 23, 52, 17, 10, 17, 50, 49, 67, 54, 27, 12, 16, 23, 45}}, {genreName:"ポップ", playingList:{3, 0, 0, 0, 0, 0, 0, 3, 1, 6, 2, 9, 7, 0, 11, 7, 8, 10, 7, 5, 3, 1, 0, 1}}, {genreName:"歌謡曲", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 2, 4, 5, 5, 4, 6, 7, 7, 1, 0, 0, 0}}, {genreName:"ヒップホップ/ラップ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ダンス", playingList:{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 0}}, {genreName:"R&B/ソウル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"チルドレン・ミュージック", playingList:{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"クラシック", playingList:{1, 1, 1, 1, 2, 4, 10, 1, 4, 3, 24, 16, 26, 1, 25, 23, 37, 40, 23, 7, 5, 8, 4, 5}}, {genreName:"ニューエイジ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0}}, {genreName:"Pop", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, {genreName:"ヴォーカル", playingList:{0, 0, 0, 0, 0, 1, 0, 0, 0, 6, 7, 0, 0, 1, 6, 1, 0, 3, 1, 0, 3, 0, 0, 0}}, {genreName:"ホリデー", playingList:{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}}, {genreName:"Folk", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}}, {genreName:"ジャズ", playingList:{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 16, 7, 3, 9, 0, 5, 10, 20, 10, 2, 1, 0, 0}}, {genreName:"インストゥルメンタル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}}, {genreName:"オルタナティブ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 0, 6, 9, 0}}}

on genreNayoseAndUnify(aList as list, genreNayoseList as list)
  set gList to FlattenList(genreNayoseList) of me
  
  
set didProc to {}
  
  
set a2List to {}
  
  
repeat with i in aList
    set aGenre to genreName of i
    
    
if (aGenre is in gList) and (aGenre is not in didProc) then
      
      
repeat with ii in genreNayoseList
        set jj to contents of ii
        
        
if aGenre is in jj then
          copy jj to {g1, g2}
          
          
          
if chkAlphabet(g1) of me = true then
            set targG to g2
            
set targG2 to g1
          else
            set targG to g1
            
set targG2 to g2
          end if
          
          
set s1Res to searchByGenreName(aList, targG) of me
          
set s2Res to searchByGenreName(aList, targG2) of me
          
          
set s3Res to addMutipleLists({s1Res, s2Res}) of me
          
          
set outRec to {genreName:targG, playingList:s3Res}
          
set the end of didProc to g1
          
set the end of didProc to g2
          
          
exit repeat
        end if
        
      end repeat
      
      
set the end of a2List to outRec
    else
      if (aGenre is not in didProc) then
        set the end of a2List to contents of i
      end if
    end if
  end repeat
  
  
return a2List
end genreNayoseAndUnify

on searchByGenreName(aList as list, aGenreName as string)
  set predicatesStr to "genreName == ’" & aGenreName & "’"
  
set anArray to (NSArray’s arrayWithArray:aList)
  
set aPred to (NSPredicate’s predicateWithFormat:predicatesStr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred)
  
  
set bbRes to first item of bRes
  
return (playingList of bbRes) as list
end searchByGenreName

on addMutipleLists(s1List)
  script spdAdd
    property s1List : {}
    
property s3List : {}
  end script
  
  
copy s1List to (s1List of spdAdd) –init
  
  
set s1Len to length of first item of (s1List of spdAdd)
  
set (s3List of spdAdd) to makeZero1DList(s1Len, 0) of me
  
  
repeat with i in (s1List of spdAdd)
    set tmpLen to length of i
    
if tmpLen is not equal to s1Len then return false
    
    
repeat with ii from 1 to s1Len
      set tmp1 to contents of item ii of (s3List of spdAdd)
      
set tmp2 to contents of item ii of i
      
      
set item ii of (s3List of spdAdd) to (tmp1 + tmp2)
    end repeat
  end repeat
  
  
return (s3List of spdAdd)
end addMutipleLists

–指定要素を指定回数追加したリストを作成する
on makeZero1DList(itemMax, itemElem)
  set allData to {}
  
repeat itemMax times
    set the end of allData to itemElem
  end repeat
  
return allData
end makeZero1DList

–By Paul Berkowitz
–2009年1月27日 2:24:08:JST
–Re: Flattening Nested Lists
on FlattenList(aList)
  set oldDelims to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {"????"}
  
set aString to aList as text
  
set aList to text items of aString
  
set AppleScript’s text item delimiters to oldDelims
  
return aList
end FlattenList

— アルファベットのみか調べて返す
on chkAlphabet(checkString)
  set aStr to NSString’s stringWithString:checkString
  
set allCharSet to 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 chkCompareString:checkString baseString:baseString
  set aScanner to 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 

Posted in list Record | Tagged 10.13savvy 10.14savvy 10.15savvy NSArray NSMutableCharacterSet NSPredicate NSScanner NSString | Leave a comment

CotEditorの表示用フォント名を環境設定から取得する

Posted on 10月 15, 2019 by Takaaki Naganoya

CotEditorの環境設定値(plist)から表示用フォント名を取得するAppleScriptです。

本来であれば、CotEditorのwindowオブジェクト自体に属性値として表示用フォント名がついているのが(GUI側からWindowごとに表示フォントを指定できるので)理想的ですが、そういう機能は実装されていないので、無理やりplistから読み取ってみました。

CotEditorの表示用フォントは、環境設定で指定したものがデフォルトで用いられ、個別のウィンドウごとに任意のフォントを指定できるようになっています。本Scriptで取得できるのは、個別のウィンドウの設定値ではなく、環境設定値のほうです。

defaultsコマンドでplistを読むのはあまりおすすめできない方法ですが、できないよりはマシというところです。

まっさらな(macOSをインストールしたての)環境にCotEditorをインストールして一度も環境設定でフォントを指定していない環境だとエラーになる(項目が存在しない)ので、その場合に備えてエラートラップを仕掛けています。


▲環境設定から表示フォント名を取得して、各行をプロポーショナルフォント使用時の画面描画幅をもとにソートする処理を書いたときに利用

AppleScript名:CotEditorの表示用フォント名を環境設定から取得する
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/10/15
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set fName to getCotEditorFontName() of me
–> "HiraginoSans-W3"

–CotEditorのplistから表示用のフォント設定(PostScript名)を取得する
on getCotEditorFontName()
  try
    set fnRes to do shell script "defaults read com.coteditor.CotEditor | grep fontName"
  on error
    return "" –インストール後に表示フォントの環境設定を一度も行っていないときにはエラーになる
  end try
  
set fName to extractStrFromTo(fnRes, "= \"", "\";") of me
  
return fName
end getCotEditorFontName

–指定文字と終了文字に囲まれた内容を抽出
on extractStrFromTo(aParamStr, fromStr, toStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set anArray to current application’s NSMutableArray’s array()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {theResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
theScanner’s scanString:fromStr intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
if theValue is missing value then set theValue to "" –>追加
    
theScanner’s scanString:toStr intoString:(missing value)
    
anArray’s addObject:theValue
  end repeat
  
  
if anArray’s |count|() is not equal to 1 then return ""
  
  
return first item of (anArray as list)
end extractStrFromTo

★Click Here to Open This Script 

hiro さんのコメントから、defaultsコマンドのオプション追加でずいぶんと簡潔に書けるようで、書き直しておきました。

AppleScript名:CotEditorの表示用フォント名を環境設定から取得する v2
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set fName to getCotEditorFontName() of me
–> "HiraginoSans-W3"

–CotEditorのplistから表示用のフォント設定(PostScript名)を取得する
on getCotEditorFontName()
  try
    set fnRes to do shell script "defaults read com.coteditor.CotEditor fontName"
  on error
    return "" –インストール後に表示フォントの環境設定を一度も行っていないときにはエラーになる
  end try
  
return fnRes
end getCotEditorFontName

★Click Here to Open This Script 

Posted in shell script | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy CotEditor NSMutableArray NSScanner | 3 Comments

青空文庫のテキストのルビタグを超高速削除

Posted on 3月 18, 2019 by Takaaki Naganoya

青空文庫のテキストのルビタグをすべて削除するAppleScriptです。

CotEditorでオープン中の青空文庫のテキストからルビタグを削除し、元のドキュメントに置換結果を反映させます。

テストに使用したのは、夏目漱石の「こころ」のテキストです。上記ページの「テキストファイル(ルビあり)」をダウンロードして、Zipアーカイブを展開して使用しました。

ファイルサイズ373KB、当該部分4,570箇所。開始文字「《」、終了文字「》」で囲われたエリアをすべて削除するという、AppleScriptにはあからさまに不得意そうな処理で、最初に書いたAppleScriptでは1分半以上かかっていました。内容は、おおよそ常識的なサブルーチンを組み合わせてループで回しただけです。わざと遅くなるように組んだりはしていません。

これを、

 (1)CotEditorからの文章テキストの取得
 (2)置換当該箇所のリストアップ
 (3)文字置換
 (4)CotEditorへの文章テキストの転送

の4つのステージに分け、それぞれ処理時間を計測。すると、(1)、(2)、(4)については1秒かかるかかからないかぐらいの速度で実行していることが判明。圧倒的に(3)文字置換の処理に時間がかかっていました。

もともと文字置換には、AppleScript処理系最速のtext item delimitersを用いるサブルーチンを使用していました。これ以上、この方向に頑張っても速く処理することはできません。一応、ダメ元で4,570個の要素を持つ巨大なtext item delimitersを作成し一括処理できないか試してみたものの、さすがに処理系のキャパシティを超過しているようで処理が戻ってきません(迷走状態)。完全にお手上げです。

そこで、AppleScriptの処理系に依存したtext item delimitersによる処理をやめ、メモリ管理効率がよくないAppleScriptのstring型のデータで保持することをやめ、置換のたびにAppleScriptのstring型に変換(cast)することをやめ、置換中は最初から最後までNSMutableStringで管理するようにしました。

このように大幅に書き換えたところ、トータルで3.58秒で処理終了するようになりました。

すべての置換が終了したあとにNSMutableStringをAppleScriptのstringに変換し、CotEditorの最前面のドキュメントに結果を転送しています。

AppleScript名:青空文庫のテキストのルビタグを削除
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/18
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property NSScanner : a reference to current application’s NSScanner
property NSOrderedSet : a reference to current application’s NSOrderedSet
property NSMutableString : a reference to current application’s NSMutableString
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch

tell application "CotEditor"
  tell front document
    set aCon to contents
  end tell
end tell

set bCon to trimStrFromTo(aCon, "《", "》") of me

tell application "CotEditor"
  tell front document
    set contents to bCon
  end tell
end tell

–開始文字と終了文字に囲われた文字列をすべて削除する
on trimStrFromTo(aParamStr, fromStr, toStr)
  script hsAry
    property anArray : {}
    
property curStr : ""
  end script
  
  
set theScanner to NSScanner’s scannerWithString:aParamStr
  
set (anArray of hsAry) to {}
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {aResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
theScanner’s scanString:fromStr intoString:(missing value)
    
    
set {bResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
if theValue is missing value then set theValue to ""
    
    
theScanner’s scanString:toStr intoString:(missing value)
    
set the end of (anArray of hsAry) to (fromStr & theValue & toStr)
  end repeat
  
  
–Case: Not found
  
if length of (anArray of hsAry) = 0 then return aParamStr
  
  
–Uniquefy
  
set (anArray of hsAry) to makeUniqueListFrom((anArray of hsAry)) of me
  
  
–Replace strings as NSMutableString
  
set (curStr of hsAry) to NSMutableString’s stringWithString:aParamStr
  
repeat with i in (anArray of hsAry)
    set j to contents of i
    
set (curStr of hsAry) to ((curStr of hsAry)’s stringByReplacingOccurrencesOfString:(j) withString:"" options:(NSRegularExpressionSearch) range:{location:0, |length|:((curStr of hsAry)’s |length|())})
  end repeat
  
  
return (curStr of hsAry) as string
end trimStrFromTo

–1D Listをユニーク化(重複削除)
on makeUniqueListFrom(theList)
  set theSet to NSOrderedSet’s orderedSetWithArray:theList
  
return (theSet’s array()) as list
end makeUniqueListFrom

★Click Here to Open This Script 

Posted in list Text | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy CotEditor NSMutableString NSOrderedSet NSRegularExpressionSearch NSScanner | 2 Comments

1D Listのうち指定文字種で構成される要素のみ抽出

Posted on 12月 20, 2018 by Takaaki Naganoya

1D List(配列)に入れた文字要素を文字種類で該当するものだけ抽出するAppleScriptです。

文字種類でデータ抽出する、という用途はけっこう多いので、単体で使えるようにしておきました。プログラムを見ていただくとわかるとおり、

 数字:”9″
 英字:”A”
 半角記号:”$”
 ひらがな:”ひ”
 カタカナ:”カ”
 漢字:”漢”

で文字種類を指定します。

以前のバージョンではありもののルーチンを組み合わせただけなので、全体的に無駄があって処理速度についてはあまり感心できないレベルだったので、若干の高速化を図りました(繰り返し処理部分で無駄な演算を省略)。

ただし、「ひらがな+カタカナは許容する」というふうに、複数の文字種を許可する例が多いので、これではまだ実用レベルには達していないと思います。

AppleScript名:1D Listのうち指定文字種で構成される要素のみ抽出
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/12/20
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property NSString : a reference to current application’s NSString
property NSScanner : a reference to current application’s NSScanner
property NSNumber : a reference to current application’s NSNumber
property NSDictionary : a reference to current application’s NSDictionary
property NSCountedSet : a reference to current application’s NSCountedSet
property NSCharacterSet : a reference to current application’s NSCharacterSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
property NSMutableCharacterSet : a reference to current application’s NSMutableCharacterSet
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch
property NSNumberFormatterRoundUp : a reference to current application’s NSNumberFormatterRoundUp
property NSStringTransformFullwidthToHalfwidth : a reference to current application’s NSStringTransformFullwidthToHalfwidth

set aList to {"Naganoya", "ながのや", "ナガノヤ", "長野谷"} –Alphabet, Hiragana, Katakana, Kanji

set aRes to filterByCharKind(aList, "A") of me –アルファベットで構成される要素のみ抽出
–> {"Naganoya"}

set bRes to filterByCharKind(aList, "ひ") of me –ひらがなだけで構成される要素のみ抽出
–> {"ながのや"}

set cRes to filterByCharKind(aList, "カ") of me –カタカナだけで構成される要素のみ抽出
–> {"ナガノヤ"}

set dRes to filterByCharKind(aList, "漢") of me –漢字だけで構成される要素のみ抽出
–> {"長野谷"}

–文字種別を判定して指定文字種のみから構成されるものを抽出
on filterByCharKind(aList as list, targCharKind as string)
  set dList to {}
  
repeat with i in aList
    set j to contents of i
    
set tmpPat to retAtrPatternFromStr(j) of me
    
if tmpPat is equal to {targCharKind} then
      set the end of dList to j
    end if
  end repeat
  
  
return dList
end filterByCharKind

–Objective-Cライクなパラメータ記述
on makeUniqueListOf:theList
  set theSet to current application’s NSOrderedSet’s orderedSetWithArray:theList
  
return (theSet’s array()) as list
end makeUniqueListOf:

–Pure AS風のパラメータ記述
on makeUniqueListFrom(theList)
  set aList to my makeUniqueListOf:theList
  
return aList
end makeUniqueListFrom

–1D Listを文字列長でソート v2
on sort1DListByStringLength(aList as list, sortOrder as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set desc1 to current application’s NSSortDescriptor’s sortDescriptorWithKey:"length" ascending:sortOrder
  
set desc2 to current application’s NSSortDescriptor’s sortDescriptorWithKey:"self" ascending:true selector:"localizedCaseInsensitiveCompare:"
  
set bArray to aArray’s sortedArrayUsingDescriptors:{desc1, desc2}
  
return bArray as list of string or string
end sort1DListByStringLength

–文字種別の判定
on retAtrPatternFromStr(aText as string)
  set b1List to {"9", "A", "$", "漢", "ひ", "カ"} –数字、アルファベット、記号、全角漢字、全角ひらがな、全角カタカナ
  
  
–set cStr to zenToHan(aText) of me
  
  
set outList to {}
  
set cList to characters of (aText)
  
  
repeat with i in cList
    set j to contents of i
    
    
set chk1 to ((my chkNumeric:j) as integer) * 1
    
set chk2 to ((my chkAlphabet:j) as integer) * 2
    
set chk3 to ((my chkSymbol:j) as integer) * 3
    
set chk4 to ((my chkKanji:j) as integer) * 4
    
set chk5 to ((my chkHiragana:j) as integer) * 5
    
set chk6 to ((my chkKatakana:j) as integer) * 6
    
    
set itemVal to (chk1 + chk2 + chk3 + chk4 + chk5 + chk6)
    
    
–if itemVal > 0 then
    
set aVal to (contents of item itemVal of b1List)
    
    
if aVal is not in outList then
      set the end of outList to aVal
    end if
    
–end if
  end repeat
  
  
return outList
end retAtrPatternFromStr

–全角→半角変換
on zenToHan(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformFullwidthToHalfwidth) |reverse|:false) as string
end zenToHan

–数字か
on chkNumeric:checkString
  set digitCharSet to NSCharacterSet’s characterSetWithCharactersInString:"0123456789"
  
set ret to my chkCompareString:checkString baseString:digitCharSet
  
return ret as boolean
end chkNumeric:

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

–漢字か
on chkKanji:aChar
  return detectCharKind(aChar, "[一-龠]") of me
end chkKanji:

–ひらがなか
on chkHiragana:aChar
  return detectCharKind(aChar, "[ぁ-ん]") of me
end chkHiragana:

–カタカナか
on chkKatakana:aChar
  return detectCharKind(aChar, "[ァ-ヶ]") of me
end chkKatakana:

–半角スペースか
on chkSpace:checkString
  set muCharSet to NSCharacterSet’s alloc()’s init()
  
muCharSet’s addCharactersInString:" " –半角スペース(20h)
  
set ret to my chkCompareString:checkString baseString:muCharSet
  
return ret as boolean
end chkSpace:

— アルファベットか
on chkAlphabet:checkString
  set aStr to NSString’s stringWithString:checkString
  
set allCharSet to NSMutableCharacterSet’s alloc()’s init()
  
allCharSet’s addCharactersInRange:({location:97, |length|:26}) –97 = id of "a"
  
allCharSet’s addCharactersInRange:({location:65, |length|:26}) –65 = id of "A"
  
set aBool to my chkCompareString:aStr baseString:allCharSet
  
return aBool as boolean
end chkAlphabet:

on chkCompareString:checkString baseString:baseString
  set aScanner to 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:

on detectCharKind(aChar, aPattern)
  set aChar to NSString’s stringWithString:aChar
  
set searchStr to NSString’s stringWithString:aPattern
  
set matchRes to aChar’s rangeOfString:searchStr options:(NSRegularExpressionSearch)
  
if matchRes’s location() = (current application’s NSNotFound) or (matchRes’s location() as number) > 9.99999999E+8 then
    return false
  else
    return true
  end if
end detectCharKind

★Click Here to Open This Script 

Posted in list regexp Text | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSCharacterSet NSCountedSet NSDictionary NSMutableArray NSMutableCharacterSet NSNumber NSNumberFormatter NSNumberFormatterRoundUp NSRegularExpressionSearch NSScanner NSString NSStringTransformFullwidthToHalfwidth | Leave a comment

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

Posted on 10月 29, 2018 by Takaaki Naganoya

WiFiの情報を元に現在地点の緯度経度情報を取得するAppleScriptです。

実行前にWiFiがオンになっている必要があるので、WiFiの状態を確認して、WiFiがオフだったらオンにする処理が必要になります。

AppleScript名:現在地点の緯度経度情報を取得する v2
— 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

–Get Current Geo Location
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.0001" as real)
  end repeat
  
  
if hitF = false then return {false, 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 

Posted in geolocation System WiFi | Tagged 10.11savvy 10.12savvy 10.13savvy CLLocationManager NSCharacterSet NSScanner | Leave a comment

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • macOS 13, Ventura(継続更新)
  • アラートダイアログ上にWebViewで3Dコンテンツを表示(WebGL+three.js)v3
  • UI Browserがgithub上でソース公開され、オープンソースに
  • Xcode 14.2でAppleScript App Templateを復活させる
  • macOS 13 TTS Voice環境に変更
  • 2022年に書いた価値あるAppleScript
  • ChatGPTで文章のベクトル化(Embedding)
  • 新発売:AppleScriptからSiriを呼び出そう!
  • iWork 12.2がリリースされた
  • 従来と異なるmacOS 13の性格?
  • 新発売:CotEditor Scripting Book with AppleScript
  • macOS 13対応アップデート:AppleScript実践的テクニック集(1)GUI Scripting
  • AS関連データの取り扱いを容易にする(はずの)privateDataTypeLib
  • macOS 13でNSNotFoundバグふたたび
  • macOS 12.5.1、11.6.8でFinderのselectionでスクリーンショット画像をopenできない問題
  • ChatGPTでchatに対する応答文を取得
  • 新発売:iWork Scripting Book with AppleScript
  • Finderの隠し命令openVirtualLocationが発見される
  • macOS 13.1アップデートでスクリプトエディタの挙動がようやくまともに
  • あのコン過去ログビューワー(暫定版)

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1390) 10.14savvy (586) 10.15savvy (434) 11.0savvy (277) 12.0savvy (185) 13.0savvy (55) CotEditor (60) Finder (47) iTunes (19) Keynote (98) NSAlert (60) NSArray (51) NSBezierPath (18) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (51) NSDictionary (27) NSFileManager (23) NSFont (18) NSImage (41) NSJSONSerialization (21) NSMutableArray (62) NSMutableDictionary (21) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (117) NSURL (97) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (56) Pages (37) Safari (41) Script Editor (20) WKUserContentController (21) WKUserScript (20) WKUserScriptInjectionTimeAtDocumentEnd (18) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • Clipboard
  • Code Sign
  • Color
  • Custom Class
  • dialog
  • drive
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • PDF
  • Peripheral
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC