簡易的な日本語テキストのParse(辞書なし)を行うAppleScriptです。
本バージョンでは、かっこ( “「”, “」”, “『”, “』”, “【”, “】”, “《”, “》”, “〈”, “〉”, “(”, “))で区切られた文字列を区分けしないで1かたまりで出力させたものです。
コマンド解釈用に作成した本Script、パラメーターとして区分けしてほしくない情報(フィールド情報やデータベース名など)をかたまりのまま出力する必要があって、そのように処理させてみました。
かっこがクロスしたりネスティング(入れ子)していることは検出していますが、そのまま連結せずに出力しています。
このプログラムを作ったことにより、固有名詞への対応のメドが立ちました。
前処理で何かの記号で固有名詞を囲えばいいんじゃないか、などと思っています。何を固有名詞とするか、ということになりますが、とりあえず住所録(Contacts.app)から人名(Last Name)や会社名をすべて出力させるのがよいだろうか、といったところです。
AppleScript名:easyJParse v4(かぎかっこ内の単語を1つの単語としてみなす) |
— Created 2018-09-26 by Takaaki Naganoya — Modified 2018-12-14 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.5" — El Capitan (10.11) or later use framework "Foundation" use scripting additions use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html property NSArray : a reference to current application’s NSArray property NSSortDescriptor : a reference to current application’s NSSortDescriptor load framework set aTargName to "曲のアーティスト名を変更" –set aTargName to "<満喜子>さんの実家から半径300メートル以内にあるコンビニを取得" –set aTargName to "Finderで選択中のAI書類上の「製品名」レイヤーから抜き出したコードをもとにスペック情報をGoogle Spreadsheet「製品コード表」から展開して保存。" set aList to parseJ(aTargName, true) of me –> {"Finder", "で", "選択", "中", "の", "AI", "書類", "上", "の", "「", "製品名", "」", "レイヤー", "から", "抜き出し", "た", "コード", "を", "もと", "に", "スペック", "情報", "を", "Google", " ", "Spreadsheet", "「", "製品コード表", "」", "から", "展開", "し", "て", "保存", "。"} return aList set aTargName to "私の名前は「長野谷」です。" set aList to parseJ(aTargName, true) of me –> {"私", "の", "名前", "は", "「", "長野谷", "」", "です", "。"} –カッコのネスティングとクロス(エラー)については、処理せずにそのまま出力 on parseJ(aTargStr as string, pickupPhraseByBracketPair as boolean) copy aTargStr to tStr set cList to characters of tStr set wList to words of tStr set cLen to length of cList set w2List to {} set w3List to {} set aCount to 0 set lastPos to 0 repeat with i in wList set j to contents of i using terms from scripting additions set anOffset to offset of j in tStr end using terms from if anOffset is not equal to 1 then set aChar to character (lastPos + 1) of aTargStr set the end of w3List to {wordList:aChar, characterList:{aChar}, startPos:(lastPos + 1), endPos:(lastPos + 1)} end if set aLen to length of j set w2List to w2List & (characters of j) set startPointer to (anOffset + aCount) set endPointer to (anOffset + aCount + aLen – 1) set the end of w3List to {wordList:j, characterList:(characters of j), startPos:startPointer, endPos:endPointer} set trimStart to (anOffset + aLen) if trimStart > (length of tStr) then set trimStart to 1 end if set tStr to text trimStart thru -1 of tStr set aCount to aCount + anOffset + aLen – 1 copy endPointer to lastPos end repeat –句読点など。文末の処理 if endPointer is not equal to cLen then set the end of w3List to {wordList:tStr, characterList:(characters of tStr), startPos:(lastPos + aCount), endPos:aLen} end if set bArray to sortRecListByLabel((w3List), "startPos", true) of me set cArray to (bArray’s valueForKeyPath:"wordList") as list –カッコでくくった範囲を1つの塊として連結する set bracketList to {"「", "」", "『", "』", "【", "】", "《", "》", "〈", "〉", "(", ")"} set bList to jointItemsBetweenBrackets(cArray, bracketList) of me return bList end parseJ –リストに入れたレコードを、指定の属性ラベルの値でソート on sortRecListByLabel(aRecList as list, aLabelStr as string, ascendF as boolean) set aArray to NSArray’s arrayWithArray:aRecList set sortDesc to NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF set sortDescArray to NSArray’s arrayWithObject:sortDesc set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray return sortedArray end sortRecListByLabel on offset of bArg in anArg set aClass to class of anArg set bClass to class of bArg if {aClass, bClass} = {text, text} then –case 1 return getOffset(anArg, bArg) of me else if {aClass, bClass} = {list, list} then –case 2 (The target case) return execOffsetList(bArg, anArg) of me else if {aClass, bClass} = {text, list} then –case 3 (Illegular case) return execOffsetList(bArg, {anArg}) of me else if {aClass, bClass} = {list, text} then –case 4 (Illegular case) return execOffsetList({bArg}, anArg) of me end if end offset –1D List同士のoffset演算を行うルーチンの本体 on execOffsetList(aList as list, bList as list) set resList to {} repeat with i in aList set j to contents of i set aCount to 1 repeat with ii in bList set jj to contents of ii if jj = j then set the end of resList to aCount exit repeat end if set aCount to aCount + 1 end repeat end repeat –見つかったItem No.が連続値かどうかチェック set sRes to chkSequential(resList) of me if sRes = true then return contents of first item of resList else return false end if end execOffsetList –与えられた1D Listが連続値かどうかをチェックする on chkSequential(aList) if length of aList = 1 then return true if aList = {} then return false set aFirst to first item of aList set aList to rest of aList repeat with i in aList set j to contents of i if j is not equal to (aFirst + 1) then return false end if copy j to aFirst end repeat return true end chkSequential –テキスト同士のoffset ofを(2.5x fasterで)実行する 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 –カッコでくくった範囲を1つの塊として連結する on jointItemsBetweenBrackets(aList as list, bracketList as list) load framework –リスト内のブラケット位置の検出 set aRes to (current application’s SMSForder’s indexesOfItems:bracketList inArray:aList inverting:false) as list –> {9, 12, 15, 18, 22, 25, 27, 29}–0 based if aRes = {} then return aList –位置情報リストを開始位置, 終了位置のペアの2D Listに変換する set cList to (current application’s SMSForder’s subarraysFrom:(aRes) groupedBy:2 |error|:(missing value)) as list –> {{9, 12}, {15, 18}, {22, 25}, {27, 29}}–0 based –カッコの位置がクロスしていないかチェック(入れ子状態はエラーになる) set dRes to checkCrossRange(cList) of me if dRes = false then return aList set ccList to reverse of cList –順次、ブラケットに囲まれた要素を連結していくので、アイテム数が随時変化する。アイテム番号が狂わないよう後方から処理する必要がある。そのために、リストの要素を逆順に組み替える –> {{27, 29}, {22, 25}, {15, 18}, {9, 12}}–0 based — copy aList to aaList repeat with i in ccList copy i to {s2Dat, e2Dat} set s2Dat to s2Dat + 1 –Array index conversion from 0 to 1 based set e2Dat to e2Dat + 1 –Array index conversion from 0 to 1 based set tmp1 to items 1 thru s2Dat of aaList set tmp2 to (items (s2Dat + 1) thru (e2Dat – 1) of aaList) as string set tmp3 to items e2Dat thru -1 of aaList set aaList to tmp1 & tmp2 & tmp3 end repeat return aaList end jointItemsBetweenBrackets –{始点, 終点}のペアの2D Listが違いにクロスしていないかチェック on checkCrossRange(aList as list) set rList to {} repeat with i in aList copy i to {sRange, eRange} set tmpRange to current application’s NSMakeRange(sRange, eRange – sRange + 1) set the end of rList to tmpRange end repeat repeat with ii in rList set jj to contents of ii repeat with i in rList set j to contents of i if jj is not equal to j then set aRes to current application’s NSIntersectionRange(jj, j) if aRes is not equal to {location:0, |length|:0} then return false end if end if end repeat end repeat return true end checkCrossRange |
More from my site
(Visited 104 times, 1 visits today)
ぴよまるソフトウェアが選ぶ、2018年に書いた「価値あるScript」 – AppleScriptの穴 says:
[…] ・easyJParse v4 簡易日本語形態素解析プログラムのアップデート版です。固有名詞に対応するための仕組みも見えてきました。 […]