ユニコードの話なんだけど、もちろん AppleScript の Unicode text について。ユニコード全般についての話ではないのでその辺りご注意を。
ちょっと、間が空いてしまいましたが...以前に Automator のワークフローのことなどについて書きましたが、そのときにちょっと引っかかったことがあったので調べていました。なにに引っかかったかというと、iPod のメモを作成する Automator のアクション。
Automator についていろいろと調べている中で、このアクションは日本語だと文字化けするというような記述をいくつか拝見しました。そうなのだろうか?と試してみたところ、なんとも言えない結果になる。化けるときもあるし、そうでもないときもある。iPod で閲覧できるときもあるし、できないこともある。
おそらく、このアクションがユニコードを扱っているところに問題があるのでしょう。できないなら解決してみようと思ったのが問題の始まり。
まず、文字化けになる主要な原因は 2 つあります。
- アクションの文字列の取り扱い(AppleScript の文字列の扱い)
- iPod の言語設定によるエンコーディングの指定
AppleScript の文字列の扱いから見てみましょう。AppleScript の文字列型は主要なところで以下の 3 種類があります。
- Unicode text
- string
- «class utf8»
Unicode text は、UTF-16 のエンコーディングになります。string は、MacEncoding として扱われ、日本語では TextEdit でのファイルの保存時に指定できる「日本語(Mac OS)」にあたります。概ね Shift_JIS です。
«class utf8» は、AppleScript で定数が与えられていないものです。UTF-8 エンコーディングにあたるのですが、定数が与えられていないので AppleScript Studio では使用できません。
Mac OS X 以前の環境との互換性を考慮するなら string を利用します。
スクリプトファイルに直接埋め込まれた文字列は、string になります。
set str to "文字列"
class of str
--> string
string は、as 演算子を使って Unicode text に変換することが可能です。«class utf8» は、Unicode text として表現されます。
set str to "文字列"
class of str
--> string
set utxt to str as Unicode text
class of utxt
--> Unicode text
set utf8 to str as «class utf8»
class of utf8
--> Unicode text
これらは比較を行うとすべて真になります。Unicode text と string、«class utf8» は、比較で違いを調べることができません。
これらはの文字列はファイルに書き出すとそれぞれ異なったエンコーディングのファイルになります。
set str to "文字列"
set theFile to (path to desktop as Unicode text) & "Test.txt"
set fh to open for access file theFile with write permission
set eof fh to 0
-- write str to fh as string -- MacEncoding
-- write str to fh as Unicode text -- UTF-16
write str to fh as «class utf8» -- UTF-8
close access fh
Unicode text で書き出したファイルは UTF-16 になるのですが、BOM は付加されません。string はこのように Unicode text や «class utf8» as で型を変換しても正しく書き出すことができますが、Unicode text や «class utf8» を string に変換するときは文字化けする可能性があります。
読み込むときは、as オプションで型を指定しない限り string としてファイルの内容を読み込みます。
read (choose file) -- MacEncoding
read (choose file) as Unicode text -- UTF-16
read (choose file) as «class utf8» -- UTF-8
これらは、 UTF-8 で保存されているファイルの内容を MacEncoding に変換して読み込むということではありません。as 演算子でエンコーディングを変更できるわけではありません。結果的に UTF-16 のファイルを as string として読み込むことで文字化けがおこるか、エラーになるか、スクリプトがその部分で停止し、終了しなくなります。型の変換は、概ね以下のようになります。
- string
- Unicode text に変換可能
- «class utf8» に変換可能
- Unicode text
- string に変換不可
- «class utf8» に変換可能
- «class utf8»
- string に変換不可
- Unicode text に変換可能
as 演算子はエンコーディングの自動変換などという機能を持っていないので、正しい型を指定しない場合、文字が化けるのは当たり前とも言えます。これは、日本語だからというものではなく、日本語だろうが英語だろうが as で型の変換を行うときは必ず発生するものです。アプリケーションが返す文字列型を把握しておき、正しい型を指定して書き出す、読み込むという基本的な注意事項が重要になります。
例えば、以下のようなスクリプト。
tell application "Safari"
text of front document
set theText to result as string
end tell
set theFile to (path to desktop as Unicode text) & "Test.txt"
set fh to open for access file theFile with write permission
set eof fh to 0
write theText to fh as string
close access fh
これは、確実に文字が化けますし、開けないファイルになります(運がよい場合は、開けます。例えば、アルファベットだけの文字列で構成されているとか)。多くのスクリプタブル Cocoa アプリケーションは、結果として Unicode text を返すものがほとんどです。Safari も同様で、返された Unicode text を as string として型の変換を行うと、その時点で文字が化けてしまいます。それをファイルに書き出しても正常なデータではないので開けないファイルが出来上がってしまいます。
上記のように Unicode text から string への変換は正しい結果を期待できません。Unicode text が返されるなら、Unicode text として扱う方が無難です。
アプリケーションが返す Unicode text...例えば、以下のようなスクリプトをいろいろなサイトで試していると、必ずしも正しい結果になっていないことがあります(文字が化けているように見える)。
tell application "Safari"
text of front document
end tell
このスクリプトの結果が文字化けするのは、Script Editor に問題があります。Script Editor ではちゃんと表示できないのです。結果が文字化けしていてもそのまま Unicode text でファイルに書き出せば、UTF-16 のファイルとしてテキストエディタできちんと表示できます。
tell application "Safari"
set theText to text of front document
end tell
set theFile to (path to desktop as Unicode text) & "Test.txt"
set fh to open for access file theFile with write permission
set eof fh to 0
write theText to fh as Unicode text
close access fh
AppleScript の Unicode text の書き出しは、BOM を付加しません。BOM を付加しておくと特にエンコーディングを指定していなくてもアプリケーションがきちんと UTF-16 のファイルとして開いてくれます(もちろん、アプリケーションの仕様によりますが)。TextEdit の環境設定で開くときのエンコーディングを指定していても、BOM がついているとわざわざエンコーディングを指定しなくても自動的に開いてくれます。BOM(Hex の FE と FF。ASCII character 254 と ASCII character 255)は、以下のようにして付加します。
tell application "Safari"
set theText to text of front document
end tell
set theFile to (path to desktop as Unicode text) & "Test.txt"
set fh to open for access file theFile with write permission
set eof fh to 0
write (ASCII character 254) to fh
write (ASCII character 255) to fh
write theText to fh as Unicode text
close access fh
上記は PowerPC での話なので Intel Mac だと逆になるのかな(よく分からないけど)。Unicode text(UTF-16)で書き出すときは、つけておく方がいいでしょう。
AppleScript で扱っている文字列からプレインテキストを取り出す方法として «class ktxt» を使う方法が紹介されていたりします。
set str to "文字列" as Unicode text
«class ktxt» of ((str as string) as record)
もちろんこの方法は、対象となる文字列が他の文字列情報を持っていないことには利用できません。string クラスに利用してもエラーになるだけですし、Unicode text であっても文字列情報を持っていない場合はエラーになるので、実際の利用には注意が必要です(この方法を使ったからといって Unicode text を string に文字化けせずに取り出せるというわけではないので。念のため)。
Unicode text と string の & 演算子による結合は Unicode text 型の文字列が結果として返ってきます。これは、AppleScript 1.10 からだったと思います。
Cocoa アプリケーションの多くでは結果として Unicode text を返す...ということは先に触れました。
tell application "TextEdit"
set theText to text of front document
class of theText
--> Unicode text
end tell
このような感じですね。しかし、ここに一つ落とし穴があります。例えば、TextEdit の書類になんらかの添付ファイルが埋め込まれているとき。上記のスクリプトでは添付ファイルの部分は「?」が代替として表示されます。この「?」が曲者で、以下のようにすると「?」以下の文章が消えてしまいます。
tell application "TextEdit"
set theText to text of front document
-->
(*"文字
画像? <-- ここに添付ファイルが存在している
文字"*)
set pList to paragraphs of theText
set theText to pList as Unicode text
-->"文字"
(*添付ファイルがある行以下が消えている...*)
end tell
不思議な現象です。寡聞にして今まで知りませんでした...。取得した文字列を行ごとに分解して iPod ノートに書き出す...ということをしていたのですが、いくらやっても結果は正しいのに空のファイルが出来上がってしまい、非常に悩みました。この問題はすべてのスクリプタブル Cocoa アプリケーションに共通します。Mail、Safari、TextEdit、Xcode、OmniOutliner...等々。
この「?」は、まさに代替で「?」のように見えるけど、文字の「?」ではないのです。ASCII number を調べると 63 が返ってくるのですが、「?」ではない。is "?" としても false が返ってくる。文字の「?」ではないので text item delimiters を使って削除することもできず、offset によるチェックも効果がない。
tell application "TextEdit"
font of attribute runs of text of front document
--> {"HiraMaruPro-W4", missing value, "HiraMaruPro-W4"}
end tell
このようにして font 属性を調べてみると missing value となる。なら、フィルタ参照で font 属性が missing value 以外のものを取得するといいのだけど、うまくいかない。
tell application "TextEdit"
attribute runs of text of front document whose font of it is not missing value
(*
{"文字
画像", "?", "
文字"}
*)
end tell
フィルタリングされないのですね。missing value が返ってくるものは、実際に調べないとそれが missing value かどうかが分からないからです。ちゃんと missing value という定数を返すアプリケーションもありますが、TextEdit のように直接指定しないと missing value を返さないアプリケーションもあります。また、missing value を返す属性は、missing value という言葉が示すようになにも返さない場合もあります。だから、以下のように missing value が返ってくることを期待してスクリプトを組んでいるとエラーになる場合があります。
set val to font of attribute run 3 of text of document 1
if val is missing value then
set val to dafaultValue
end if
--> 変数 val が定義されていません
属性が設定されていないときはなにも返されないというときは try 構文で対処するしかないです。
set val to font of attribute run 3 of text of document 1
try
val
on error
set val to defaultValue
end try
少し話が逸れましたが、こういうこともあるということで。添付ファイルを表す「?」は、以下のようにして取り除くことができます。
tell application "TextEdit"
set attrs to font of attribute runs of text of front document
set tmp to {}
repeat with i from 1 to count attrs
set attr to item i of attrs
if attr is not missing value then
set end of tmp to attribute run i of text of front document
end if
end repeat
tmp
end tell
これで添付ファイルを表す「?」を取り除くことができますが、問題は長い文章だった場合。非常に時間がかかる。正直、こういうことで悩むなどと思っていませんでした。なにかさくっと「?」だけを取り除く簡単な方法はないのでしょうか。
以上が AppleScript における文字列の取り扱いの基本的なところでしょうか。さて、iPod 上でファイルを閲覧するには iPod 側の問題も知っておく必要があります。
iPod の言語が「日本語」のとき、ファイルのエンコーディングは「MacJapanese」が使用されます(iPod 内部で)。「日本語」以外に「韓国語」、「簡易体中国語」、「繁体字中国語」は、それぞれに対応したエンコーディングが利用されます。これら以外の時は、すべて Latin1 のエンコーディングが使用されます。UTF-8 と UTF-16 は、BOM によってエンコーディングを判定させることができます。
iPod の仕様がこのようになっているので、日本語で iPod にノートを表示させるならファイルの方は「日本語(Mac OS)」(MacJapanese)で保存するのがいいでしょう。Unicode text として保存しているなら、
- BOM を付加する
- ファイルの最初にエンコーディングタグを付加する
いずれかの方法で文字化けを回避できるかもしれません。エンコーディングタグは、ファイルの先頭に以下のようなタグを付加することでファイルのエンコーディングを直接指定するものです。
<?xml encoding="MacJapanese"?>
この場合、Unicode text を書き出しているのだから
Automator のアクションを使って iPod にノートを書き出したときになんらかの問題がある場合、
- Automator 側に問題がある(AppleScript の文字列の扱い)
- iPod の設定に問題がある
のいずれかになります。一概に日本語だから AppleScript でうまく処理できないのが原因とも言えません。Script Editor 上で書き出す限り、きちんと書き出せているのですから。問題は途中で as string としてみたり as Unicode text としてみたり、書き出すときの as オプションがおかしかったり...そういったところにあって、これは対象が日本語でも英語でも同じように文字化けします。この辺りは Automator のアクションの組み合わせによっておこる可能性があります(前のアクションが文字列を加工してし、string 型の文字列を渡した等)。
以上が iPod でノートが表示されないときの主要な原因になります。
Automator の「新規 iPod メモ」アクションは、UTF-16 のファイルを書き出します。これは、TextEdit などで開いてみることができます。しかし、BOM は付加されていません。ですので、iPod の「言語」設定が「日本語」の場合、iPod は「日本語(Mac OS)」でファイルを表示しようとします。ここでファイルのエンコーディングと iPod のエンコーディングの違いが生じるため、
- 内容が表示されない
- 文字化けが起きる
のいずれかの結果になります。「新規 iPod メモ」アクションが書き出したファイルの先頭にエンコーディングタグか BOM をつければいいのですが、Automator のアクションの中にはそういうものがありません。書き出しされたファイルを TextEdit で開き UTF-16 で別名で保存するのが手っ取り早いでしょうか(TextEdit で UTF-16 エンコーディングを指定して保存すると BOM が付加される)。
結局のところ「新規 iPod メモ」アクションは、常にうまく表示できるファイルを作成できるわけではない、という問題を孕んでいるのですね。