parseIntとparseFloatの覚え書き

前フリが思いつかない件について。
この記事の主旨は一言で言うと「parseIntには第2引数で基数を指定しよう」です。

基本

parseIntとparseFloatは、どちらも引数として渡された文字列をパースして数値に変換するグローバル関数。
parseIntは整数、parseFloatは小数と整数の両方(浮動小数点数)を扱える。

parseInt("100")    // 100
parseInt("3.14")   // 3

parseFloat("100")  // 100
parseFloat("3.14") // 3.14

パース方法とNaN

parseIntとparseFloatは、どちらも引数の文字列を先頭から1文字ずつ確認し、数値と解釈できない文字が見つかった時点で、その直前までの部分を数値として返す。
一文字も数値にできなかった場合はNaN(数値ではないことを表す数値型の値)を返す。

わかりにくいので例を。

parseInt("100pt")     // 100
parseInt("-3.14pt")   // -3
parseInt("314e-2")    // 314
parseInt("hoge123")   // NaN

parseFloat("100pt")   // 100
parseFloat("-3.14pt") // -3.14
parseFloat("314e-2")  // 3.14
parseFloat("hoge123") // NaN

parseFloatが数値と解釈できるのは、正負符号(+/-)、0〜9の数字、小数点、指数(e)。
parseIntはそこから小数点を除いたもの……ではなくて、実はもうちょっと複雑。

parseIntの第2引数(基数)

parseIntの引数として、パースしたい文字列のほかに、「何進数として解釈するか」という基数を渡すことができる。
基数は文字列でなくて数値で渡すことに注意。

parseInt("15", 10) // 15
parseInt("15", 8)  // 13
parseInt("15", 16) // 21

同じ"15"という文字列を、10進数、8進数、16進数でパースした値が返ってくる。

基数の指定によって、数として解釈できる文字は変わってくる。ちょっと複雑と言ったのはこのこと。

parseInt("19e", 10) // 19("e"はパース不可)
parseInt("19e", 8)  // 1 ("9e"はパース不可)
parseInt("19e", 16) // 414

基数を省略した場合、通常は10進数でパースされる。のだけど、省略するとけっこう危険だったりする。

基数を省略した場合

もしくは、基数に0を指定した場合、自動的に以下のように解釈されることが多い。

  • "0x"または"0X"で始まる文字列が渡された場合、基数は16(16進数)と解釈される。
  • "0"で始まる文字列が渡された場合、基数は8(8進数)と解釈される。
  • その他の文字で始まる文字列が渡された場合、基数は10(10進数)と解釈される。
parseInt("0x15") // 21(16進数として解釈)
parseInt("015")  // 13(8進数として解釈)※実装による
parseInt("15")   // 15(10進数として解釈)


16進数はともかく、この8進数がなかなか厄介で……

parseInt("015")  // 13(8進数)※実装による
parseInt("019")  // 1 (8進数、"9"はパース不可)※実装による
parseInt("08")   // 8 (8進数だとNaNになるので10進数で解釈)※実装による


あるでしょう、ノンブルとかで先頭をゼロで埋めてるやつ。
それをうっかりparseInt(textframe.contents)なんてやってしまうと、ページによって変な数値が返ってくる可能性があるということ。

余談

先頭の文字列が"0x"なら16進数、というのは、JavaScript(というか、ECMAScript)の仕様として規定されている。しかし実は先頭が"0"の場合は、「8進数にしてもいいよ(どっちかいうと10進数でやってね)」ということになっていた。
実装では8進数を採用したものが多いけど、確実ではないらしい。つまり実行する環境によって結果がまちまちということ。さっきから※実装によるって書いてるのはそういうことです。

いちおう現在の最新の仕様では、"0x"と"0X"を16進数とするのを除けばデフォルトは10進数、と定められている。けど実装のほうは今のところほとんど追いついてないらしい。AdobeのESTKでも8進数になるしね。

というわけで、この記事の主旨


parseIntには第2引数で基数を指定しよう


大事なことなので2回いいました。ちなみにparseFloatは常に10進数で、基数指定はできません。

JavaScriptのArray.sort覚え書き

お蔵出しシリーズ3。いつだか忘れたけど昔書いて、2013年5月にリライトしたらしいものをほぼそのまま。どうでもいいけどこのシリーズ、タイトルを統一しようとする気持ちがまったく見受けられませんね。

基本

array.sort(比較関数);

配列arrayの要素を、比較関数(省略可)に基づいて並べ替える。並べ替えは破壊的に行われる、つまり配列array自体が並べ替えられて、元の順番は保存されない。

比較関数を省略した場合

配列arrayの各要素を文字列として比較し、辞書順に並べ替える。

たとえば以下のように処理される。

var array = [10,9,5,15,60];
array.sort();

alert(array); // [10,15,5,60,9] ←数値順ではない
alert(typeof array[0]); // number ←要素としてはnumberのまま

数値順に並べ替えたい場合、あるいはもっと複雑な比較を行って並べ替えたい場合は、比較関数の指定が必要。

比較関数

比較関数には2つの要素が引数として渡され、戻り値に応じてその要素の順序が入れ替えられる。仮に比較関数の第一引数をa、第二引数をbとしたとき、

戻り値 < 0 aが前に来るように並べ替えられる
戻り値 == 0 保証なし(順序を入れ替えないことになっている)
戻り値 > 0 bが前に来るように並べ替えられる

という処理が行われる。

function compare(a, b){
  if(aがbより小) {
    return -1; // 負の数であればなんでもいい
  }
  if(aがbより大) {
    return 1;  // 正の数であればなんでもいい
  }
  return 0;
}

array.sort(compare);

compareを別に定義せず、sortメソッド実行時に指定してもよい。無名関数でもOK。

array.sort(function(a, b){
  if(aがbより小) { return -1; } // 負
  if(aがbより大) { return 1;  } // 正
  return 0;
});

要するに、「こういう基準で要素を並べ替えてね」というのをsortメソッドに教えるためのもの。

例:数値の配列を数値として小さい順に並べ替えたいとき
var array = [10,9,5,15,60];

array.sort(function(a, b){ return a - b; });

alert(array); // [5,9,10,15,60] ←数値順

aとbどちらも数値であれば減算できる。aがbより小さければ式「a - b」の結果は負の数になるので、そのまま戻り値にしている。

例:オブジェクトの配列を、各オブジェクトのプロパティ「value」の値が大きい順に並べ替えたいとき
array.sort(function(a, b){
  if(a.value > b.value) { return -1; } // a.valueが大きければ負
  if(a.value < b.value) { return 1;  } // a.valueが小さければ正
  return 0;
});

これだけ覚えろ


aをbよりに置きたいときは戻り値をにする

JavaScriptのループ文まとめ

お蔵出しシリーズ2。2008年2月に書いたらしいものに加筆したら原型がなくなりました。

ループ文とは

通常は上から下に処理が進んでいくプログラムを、一定条件に当てはまるあいだだけ繰り返し処理する制御文。

for文

初期値、条件、増減式を設定して、条件が真であるあいだ、一定の処理を繰り返す命令。
正確に言うと条件が偽になったら処理をしないでループを抜ける、ってことになる。


ループ用の変数に初期値(数値)を代入して、それを増減してループ回数を制御する。なので、主にループ回数が開始前に設定できる場合に使う。

for( 式1 ; 式2 ; 式3 ) {
  繰り返したい処理
}
式1
初期値を設定する式。ループ開始前に一度だけ評価される。
式2
条件式。毎回のループの最初に評価され、もしも偽(false)だったら{}内の処理を行わずにループを抜ける。
式3
ループ用の変数の値を増減する式。後処理。毎回のループの最後に評価される。式2が偽になった場合はその時点でループを抜けてしまうので、この式は実行されない。


ちなみに、式1と式3には複数の式を書くことができる。その場合は式をカンマ , で区切ること。*1
例えば「配列arrの要素を順番にすべて書き出す」みたいな処理をしたい場合、

var arr = [10, 20, 30, 40];
for( var i = 0 ; i < arr.length ; i++ ) {
  alert( arr[i] );
}
// 結果:「10」「20」「30」「40」

って書いてもいいんだけど、これだと式2にあたる「 i < arr.length 」の評価時にいちいち配列の要素数を調べることになる。

var arr = [10, 20, 30, 40];
for( var i = 0 , arr_len = arr.length ; i < arr_len ; i++ ) {
  alert( arr[i] );
}
// 結果:「10」「20」「30」「40」

こうすれば、要素数を調べる処理はループ開始前の一回だけで済む。ループ回数が最初に固定できる場合は、採用すると処理が早くなるかも。

while 文

条件を設定し、それが真である間、一定の処理を繰り返す命令。

while( 条件式 ) {
  繰り返したい処理
}

初期値の設定や後処理がないので、ループ内で条件を変更するか、break(後述)とかでループを抜ける必要がある。でないと無限ループになる。

while(true) {
  breakがなければ無限ループされる処理
}


forのところで書いた「配列arrの要素を順番にすべて書き出す」処理をwhile文でやる場合、

var arr = [10, 20, 30, 40];
var i = 0, arr_len = arr.length;
while( i < arr_len ) {
  alert( arr[i] );
  i++;
}
// 結果:「10」「20」「30」「40」

って書くと、for文とまったく同じことができる。


もうちょっと抽象化すると、

for( 式1 ; 式2 ; 式3 ) {
  繰り返したい処理
}

式1;
while( 式2 ) {
  繰り返したい処理
  式3;
}

が同じということ。
でもwhileでこれをやるくらいなら素直にfor使えばいいと思う。


whileの場合、たとえばarrを破壊してしまってもいいなら以下のようなことができる。

var arr = [10, 20, 30, 40];
while( arr.length ) {
  alert( arr.shift() );
}
// 結果:「10」「20」「30」「40」

Array.shift()は配列から最初の要素を取り除き、その要素を返す。
ループのたびに配列arrから要素がひとつずつ減っていって、要素がなくなったら次のループ開始時には条件式「 arr.length 」が0(真偽値に変換するとfalse)になるので、ループを抜ける。
複数行にわたる文字列を取得して、一行ずつ最後まで処理したい場合なんかに使える。

do-while 文

while文と似てるけど、これは一度は必ず一定の処理を行い、その後、条件が真のあいだだけ同じ処理を繰り返す場合に使う命令。

do {
  一度は必ず行うけど、もしかしたら繰り返したいかもしれない処理
} while( 条件式 )

for文やwhile文の場合、条件式が最初からfalseだったらその時点でループを抜けてしまうので、{}内の処理は一度も行われない。
do-while文では、条件式は{}内の処理が行われた後に評価される。なので条件式の結果がどうであっても、とりあえず一度は必ず処理が行われる。

つっても、今までこれ使う機会は一度もなかったんだが……

break文、continue文

ループの途中で処理の流れを中断したい場合に使う命令文。
breakの場合は処理を中断し、直ちにループを抜け、次の処理に移る。

for( var i = 0 ; i < 10 ; i++ ) {
  if( i==5 ) {
    break; // forループを脱出
    alert( "ここは処理されない" );
  }
  alert( "i = " + i );
}
alert( "ループ外 i = " + i );
// 結果:「i = 0」「i = 1」「i = 2」「i = 3」「i = 4」「ループ外 i = 5」

breakでループを抜けた場合、ループ終了時の処理(「i++」の部分)は行われていない。

continueの場合は、直ちにその回のループ終了処理を行う

for( var i=0 ; i<10 ; i++ ) {
 if( i==5 ) {
    continue; // ループ1回分をここで終了する
    alert( "ここは処理されない" );
  }
  alert ( "i = " + i );
}
alert( "ループ外 i = " + i );
// 結果:「i = 0」「i = 1」「i = 2」「i = 3」「i = 4」「i = 6」「i = 7」「i = 8」「i = 9」「ループ外 i = 10」

while文の場合は、条件の変更前にcontinueを置いてしまうと無限ループになるので注意。

for-in文

for-in文はfor文から派生した構文。
配列の各要素……というか、オブジェクトの各プロパティにアクセスして、同じ処理を繰り返す場合に使う。

var arr = [10, 20, 30, 40];
for( var i in arr ) {
  alert( arr[i] );
}
// 結果:「10」「20」「30」「40」すべての要素を列挙

ちなみに i にはオブジェクトのプロパティ名(文字列)が入る。

var arr = [10, 20, 30, 40];
for( var i in arr ) {
  alert( i + " : " + typeof i );
}
// 結果:「0 : string」「1 : string」「2 : string」「3 : string」

数値に見えるけど文字列。


例は配列で書いたけど、実はfor-inは配列に使わないほうがいい。
以下は理由。わからなければ「使っちゃいけないのねフーン」でいいと思う(よくないけど)。


for-in構文は「オブジェクトのプロパティ」にアクセスするので、例えば他のところでArrayオブジェクトが拡張された場合、そのプロパティまで列挙してしまう。

Array.prototype.hoge = function(){ alert("ほげー"); }; // Arrayオブジェクトを拡張
var arr = [10, 20, 30, 40];

for( var i in arr ) {
  alert( arr[i] );
}
// 結果:「10」「20」「30」「40」「function(){ alert("ほげー"); }」最後のはhogeの中身が見えてる

オブジェクト(インスタンス)自身が持っているプロパティだけを列挙したいときは、都度hasOwnProperty()で判定すればOK。

Array.prototype.hoge = function(){ alert("ほげー"); }; // Arrayオブジェクトを拡張
var arr = [10, 20, 30, 40];

for( var i in arr ) {
  if(arr.hasOwnProperty(i)){
    alert( arr[i] );
  }
}
// 結果:「10」「20」「30」「40」hogeはarrが持っているプロパティじゃないのではじかれる
var arr = [10, 20, 30, 40];
arr.hoge = function(){ alert("ほげー"); }; // arrに対してhogeメソッドを追加

for( var i in arr ) {
  if(arr.hasOwnProperty(i)){
    alert( arr[i] );
  }
}
// 結果:「10」「20」「30」「40」「function(){ alert("ほげー"); }」hogeも列挙される

for-inのループはプロパティを定義した順番で行われる(正確には、プロパティに値を代入した順番)。
また、プロパティの値が定義されていないものは無視される。

var obj = new Object;
obj.age;               // 宣言だけはしたけど未定義(undefined)
obj.name = "権兵衛";
obj.age  = "20";       // ここで値を代入
obj.addr = "ちば";
obj.hobby;             // 宣言だけはしたけど未定義(undefined)
for( var i in obj ) {
  alert( i + " : " + obj[i]);
}
// 結果:「name : 権兵衛」「age : 20」「addr : ちば」hobbyは出てこない


疲れたのでこの辺で切り上げ。

*1:正確に言うと、式1〜式3のそれぞれが式であればいいので、複数の式をカンマ演算子でつないで一つの式にしているというだけ。式2も同じことができるけど、評価後の値によって分岐するので順番が大事になる。ループ毎に処理したいものは素直に式3に書いたほうがいい。

JavaScriptの条件演算子(三項演算子)

PCの中身を整理したので、お蔵出しシリーズと称して、昔自分のために書いた文章を供養していこうと思います。たぶんあんまり続かない。
今回は2008年2月に書いたらしいものに加筆修正しました。

基本(条件演算子の記法)

if(条件式) {
  条件式が真(true)の場合の処理
}
else {
  条件式が偽(false)の場合の処理
}

っていう文と同じ処理をする式を、

(条件式) ? 条件式が真の場合に評価する式 : 条件式が偽の場合に評価する式

と書くことができる。


?:を条件演算子という。JavaScriptでは唯一、項(被演算子)が三つの演算子なので、三項演算子とも呼ばれる。


条件式のカッコ()は可読性のために入れただけで、必須ではない。同じくスペースも必須ではない。

  • それぞれの項はすべて式である
  • 全体が一つの式になっている
  • 式なので全体をカッコ()でくくってもよい

という点に注目。といってもこれは演算子が作る式ならすべてに当てはまるのだけど。

条件式 ?
条件式が真の場合に評価する式 :
条件式が偽の場合に評価する式

とかなんとか、途中で改行してもOK。

式の評価結果

全体で一つの式になっているので、全体で一つの値を返す。
返す値は最後に評価された式の値になる。


たとえば、変数aに(xの値に応じて)bまたはcを代入したい場合、if文を使うとこんな風になる。

x = 10;
if(x == 10) {
  a = b;
}
else {
  a = c;
}
// 結果:aにbが代入される

これを省略して

x = 10;
(x == 10) ? a = b : a = c;
// 結果:aにbが代入される

と書けて、さらに省略して

x = 10;
a = (x == 10) ? b : c;
// 結果:aにbが代入される

と書ける。
演算子の優先順位にもとづき、代入演算子=より条件演算子?:のほうが先に評価されるので、「(x == 10) ? b : c」という式の値がaに代入される。


最後の書き方は個人的によく使ってる。変数に代入するという目的が目に見えやすい気がするので。


あとは、たとえば「引数が条件に合うかどうかを真偽値で返す関数」が

function hoge(huga) { return (huga == piyo) ? true : false; }

とか書けたりする。
if文が増えないからすっきり。でもこっちはやり過ぎるとたぶんあとで読めない。

応用1:入れ子にもできる

全体が式なので、入れ子にもできる。たとえば

if(x < 10) {
  y = a;
}
else if(x < 20) {
  y = b;
}
else if(x < 30) {
  y = c;
}
else {
  y = d;
}

は、

y = (x < 10) ? a : (x < 20) ? b : (x < 30) ? c : d;

と書ける。改行もできるので

y = (x < 10) ? a :
    (x < 20) ? b :
    (x < 30) ? c :
    d;

とか書いてもいい。
素直にifかswitchで書いたほうが、条件分岐ってことがわかりやすくていいと思う。人の書いたコードで見つけたときに面食らわない程度におぼえておく。

応用2:配列/ハッシュの添え字にもできる

くどいようだが式なので、配列やハッシュの添え字にもできる。

var arr = new Array("あ","い","う","え");
var x = 10;

alert(arr[(x==10)?1:3]); // 結果:「い」

使う必要ないと思う。役に立つのはブックマークレットを作るときくらいかな。

選択した文字列を索引項目に登録するJavaScript(読み仮名自動入力、CS3〜)

はいこんばんは。

InDesignで索引項目を追加するときはソートのために読み仮名を入力しなくてはならないのですが、手打ちするのがあまりにもめんどくさいので自動的に取得するスクリプトを作りました。
読み仮名の取得にはみんな大好きYahoo!のテキスト解析WebAPIを利用しています。

これを


こうします


あくまで自分用に作ったものなので、

  • 参照形式はデフォルトの「現在のページ」のみ
  • 項目のレベルは設定できない(すべてレベル1になる)

という仕様になっています。
あと、APIのアプリケーションIDは消してありますので、もし使用する場合は自分のアプリケーションID(ランダムな英数字になってます)を取得して、「◆◆◆ココにアプリケーションIDを書く◆◆◆」のところに入れてください(1箇所だけです)。YahooのIDを作れば誰でも取得できます。
ちょっと試してみたいという人にはだいぶ不親切ですがご了承ください><


このスクリプトを書くにあたって、2つの記事を大いに参考にさせていただいています。


動作確認は、WinXP + CS4、WinXP + CS5.5、Win7 + CS5.5、MacOSX 10.7.5 + CS3、MacOSX 10.5.8 + CS3、MacOSX 10.5.8 + CS4で行いました。Macでの動作確認にご協力いただいたお二方、ありがとうございました! 遅れてすいません!


■使用前の確認事項

  • なにが起きても泣かないようにデータのバックアップを取りながら使用してください(最重要)。
  • インターネットへアクセスできる環境が必要です。
  • Yahoo!の「日本語形態素解析API」を使用しています。サービスが終了した場合、スクリプトも使用できなくなります。
  • 選択中の文字列をインターネット経由でYahoo!のサービスへ送信します。機密情報などが漏洩した場合でも責任は持ちません。
  • アクセスログなどを見れば、どんな文字列を送信したか誰でもわかります。
  • 記号類は読み仮名から削除されます。


■使用中の注意事項

  • APIが期待通りの読み仮名を返してくるとは限らないので、必ず1件ずつ確認しながら登録してください。
  • 登録直後は索引項目のページ数が表示されません。索引パネルのメニューから[プレビューを更新]を実行すると直ります。
  • 索引登録時、ドキュメントの最初のページ付近にダミーのテキストフレームが作られます。通常は自動的に削除されますが、スクリプトが途中で止まった場合などに残る可能性があります。ひととおり登録したら、フレームが残っていないか確認してください。あったら消してね。
  • なにが起きても泣かないようにデータのバックアップを取りながら使用してください(念押し)。
// 索引登録支援ツール(InDesign CS3〜)
// Yahoo!のテキスト解析APIを利用して索引の読み仮名入力を自動化します。
// http://developer.yahoo.co.jp/webapi/jlp/
//
// 以下の記事をパク^H^H大いに参考にさせていただいています。
// kmutoさん
// via http://d.kmuto.jp/20120912.html
// CLさん
// via http://d.hatena.ne.jp/C_L/20081012/indesign_socket_http
//
// v0.9 2013/04/03
// v1.0 2013/06/03  デフォルトスタイルの初期化処理追加、自分で使用開始
// v1.1 2013/06/17  初期化処理をやめてダミー文字のサイズだけ指定する形に変更
// 
// NYSL http://www.kmonos.net/nysl/
// ==============================================================================


main();


// メインの処理
function main(){

  //Yahoo!APIのアプリケーションID
  var myAppID = "◆◆◆ココにアプリケーションIDを書く◆◆◆";

  // 選択状態チェック(テキストオブジェクトを選択してる状態のみ動作)
  if(app.selection.length == 0 || !app.selection[0].constructor.name.match(/^(Text|Word|Character|Paragraph|Line|TextColumn|TextStyleRange)$/)){
    alert("索引登録可能なテキストを選択してください。");
    return false;
  }

  var actDoc = app.activeDocument;
  var idx = (actDoc.indexes.length > 0) ? actDoc.indexes[0] : actDoc.indexes.add();

  var targ = app.selection[0]; // 選択中のテキスト
  var title = targ.contents;   // 索引項目になる文言

  // APIリクエスト
  var yapiObj = new YAPIReading();
  yapiObj.appid = myAppID;
  var reading = yapiObj.getReading(title); // 読みがなになる文言

  // 表示ダイアログ準備
  // ダイアログで読みがなを修正可能
  var dlg = createDialog();
  dlg.tf.text = title;   // 索引項目欄に入力
  dlg.rf.text = reading; // 読みがな欄に入力
  // 読みがなが空文字(APIがエラー返してきてる)だったらメッセージを上書きする
  if(reading == ""){ dlg.info.text = "読みがなの自動取得に失敗しました。直接入力してください。"; }

  // ダイアログ表示から登録実行
  // 登録ボタンを押すと、その時点の読みがな欄のテキストを読みがなとして登録
  if(dlg.show() == 1){
    title = dlg.tf.text;
    reading = dlg.rf.text;

    try{
      embedIndex(idx, targ, title, reading); // 登録実行
    } catch(e) {
      alert("登録に失敗しました。\n索引項目または読みがなに使えない文字がないか確認してください。");
      arguments.callee();
    }

    return true;
  }

  return false; // 登録しなかったらfalse返すことにしておく(なんとなく)
}





// ***************************************************************************
//
// 以下、関数・オブジェクト定義など
//
// ***************************************************************************


// 索引を追加する関数 ********************************************************
// kmutoさんのアイディア(マーカーのコピペ)を拝借
// via http://d.kmuto.jp/20120912.html
// ***************************************************************************
function embedIndex(index, target, title, reading) {

  // ダミーのテキストフレームを作って★マークとか入れておく
  var dummyFrame = index.parent.pages[0].textFrames.add();
  dummyFrame.geometricBounds = [0, 0, 50, 50]; // サイズは適当
  dummyFrame.insertionPoints[0].pointSize = 1; // 文字あふれ対策にサイズを小さくしておく
  dummyFrame.contents = "★";

  // 項目追加
  // ダミーの★マークのところに索引マーカーを入れる
  index.topics.add(title, reading).pageReferences.add(dummyFrame.characters[0]);

  // マーカー文字をカット&ペーストして正しい位置に移動
  dummyFrame.characters[0].select();
  app.cut();
  target.insertionPoints[0].select();
  app.pasteWithoutFormatting(); // フォーマットなしでペースト

  // ダミーのフレームを始末
  dummyFrame.remove();
}



// ダイアログオブジェクトを作って返す関数 ************************************
// あとで部品にアクセスしやすいようにショートカット作ってある
// dlg.tf   : 項目入力欄
// dlg.rf   : 読みがな入力欄
// dlg.info : 情報欄
// ***************************************************************************
function createDialog(){
  var dlg = new Window("dialog", "索引登録");
  dlg.orientation = "row";
  dlg.alignChildren = "top";

  var inputG = dlg.add("group");
  inputG.orientation = "column";
  inputG.alignChildren = "left";
  var infoLabel = inputG.add("statictext", undefined, "読みがなを修正してください。キャンセルすると登録を中止します。");

  var inputTitle = inputG.add("group");
  inputTitle.orientation = "row";
  var titleLabel = inputTitle.add("statictext", undefined, "索引項目:");
  var titleField = inputTitle.add ("statictext", undefined, undefined);
  titleLabel.characters = 9;
  titleField.characters = 35;

  var inputReading = inputG.add("group");
  inputReading.orientation = "row";
  var readingLabel = inputReading.add("statictext", undefined, "読みがな:");
  var readingField = inputReading.add ("edittext", undefined, undefined);
  readingLabel.characters = 9;
  readingField.characters = 35;

  var buttonG = dlg.add("group");
  buttonG.orientation = "column";
  buttonG.add("button", undefined, "登録", {name: "ok"});
  buttonG.add("button", undefined, "キャンセル", {name: "cancel"});

  // ショートカット定義
  dlg.tf = titleField;
  dlg.rf = readingField;
  dlg.info = infoLabel;

  return dlg;
}



// 読みがな取得用クラス定義 **************************************************
// アプリケーションIDはインスタンス側で設定する
//
// var hoge = new YAPIReading();      // インスタンス作成
// hoge.appid = "★★アプリケーションID★★"; // 自分のアプリケーションIDを指定する
// var title = "僕の妹は漢字が読める";     // 読みたい文言
// var reading = hoge.getReading(title);     // 解析結果を取得
//
// ってする
// 記号類はfilterで除去。これも変更可能
// 読みがな部分の抽出はテキストそのまま正規表現でぶっこぬき。XML解析なにそれおいしいの
// ***************************************************************************
function YAPIReading(){
  this.appid    = "";
  this.filter   = "1|2|3|4|5|6|7|8|9|10|11|12";
  this.response = "reading";
  this.results  = "ma";

  this.rex = /<reading>(.*?)<\/reading>/g; // <reading>要素を見つける正規表現(gオプション付き)

  this.getReading = function(sentence){
    var reading = "";

    var requestURI = 'http://jlp.yahooapis.jp/MAService/V1/parse?'
                   + 'appid=' + this.appid
                   + '&ma_filter=' + this.filter
                   + '&response='  + this.response
                   + '&results='   + this.results
                   + '&sentence='  + encodeURI(sentence);

    var lwp = new Lwp();
    var result = lwp.get(requestURI);

    // gオプション付きRegExpオブジェクトのexecループ
    // <reading>要素を見つけるたびに中身のテキストを足していく
    var m;
    while (m = this.rex.exec(result)) {
      reading += m[1];
    }
    return reading; // リクエストに失敗した場合はreadingタグがないので空文字になってるはず
  }
  return this;
}


// HTTPアクセス(GET)用クラス定義 *******************************************
// CLさんのモジュールを微改造
// via http://d.hatena.ne.jp/C_L/20081012/indesign_socket_http
// prototypeをやめてUser-Agentをそれっぽくしただけ
// ***************************************************************************
function Lwp() {
  this.userAgent = "InDesign/" + app.version + " (InDesign " + app.version + "; " + $.os + "; ja)";
  this.uri = function(uri) {
    var rex = new RegExp('http://([^:/]+)(?::(\d+))?(.+)');
    // via http://pc11.2ch.net/test/read.cgi/php/1015692614/57
    var urlObj =[];
    if ( uri.match(rex) ) {
      urlObj.host = RegExp.$1;
      urlObj.port = RegExp.$2 ?RegExp.$2 :80;
      urlObj.path = RegExp.$3;
    }
    return urlObj;
  }
  this.get = function (uri) {
    var conn = new Socket;
    var urlObj = this.uri(uri);
    if ( conn.open(urlObj.host + ':' + urlObj.port, 'UTF-8') ) {
      conn.write ("GET " + urlObj.path + " HTTP/1.0\n" + "Host: " + urlObj.host + "\n" + "User-Agent: " + this.userAgent + "\n\n");
        var reply = conn.read(999999);
        conn.close();
      return reply.substring(reply.indexOf("\n\n") + 2);
    }
  }
  return this;
};

なんとなく、使っているツール(フリーウェア)を晒してみる

はいこんばんは。

4GBパッチというものの存在を知ってちょっと調べていたのですが、その過程でこの記事を見かけまして。
4GBパッチは効果がある!  & 個人的使用ツール
せっかくなので自分の愛用しているソフトウェアをいくつか晒してみたいと思います。
当然すべてWindows用です。

テキストエディタ

サクラエディタUnicode

メモ書きから置換処理、Grep検索、ちょっとしたスクリプト書きまでこれ一つで済んでしまいます。マクロもあるよ。
設定項目が膨大にあるけど、その分かゆいところをゴリゴリカスタマイズできて好きです。デフォルトでも十分使えますけどね。
昔は内部がShift-JISだったのですが、いまはUnicode版もあります。

ファイルリネームツール

お〜瑠璃ね〜む

http://beefway.sakura.ne.jp/dl-allrename.html
仕事しはじめて最初に使ったのがこれで、そのまま使い続けてます。正直ほかのやつ使ったことないので、どこがいいとかアピール難しい……
一通りの機能が揃っていながら、あんまり悩まずに直感的に使える素直さが魅力ですかね。リネーム前のプレビューが見やすいので、致命的なミスも回避できます。
開発はちゃんと継続されていて、たまにバージョンアップされてます。

CopyExt(拡張コピー)

http://www.htosh.com/software/freesoft/copyext.html
エクスプローラ拡張。ファイルやフォルダの右クリックメニュー(コンテキストメニュー)から、

  • リネームしつつコピー
  • 特定のフォルダにコピー
  • タイムスタンプを比較してコピー
  • まとめて選択して、フィルタを通ったものだけをコピー

などなど、いろんな方法でファイルコピーを行えます。設定をプロファイルで保存しておけば、複雑なコピーも一発で実行できたり。
たとえば私は、「1つ以上のファイルを選択した状態で、マウス右ボタンを押しながらドラッグ→表示されるコンテキストメニューで[ここに拡張コピー]をクリックすると“backupXX_(元ファイル名)”という名前でそれぞれ複製される」という設定で使っています。XXは連番で、すでに同じ名前のファイルがある場合だけ番号が増えていきます。所要時間1.5秒。
これでぽいぽいバックアップ取っていけば万が一の時も安心。エクスプローラ拡張なので、ファイルの保存ダイアログ上でも有効。別名保存時に新しい日付のファイル名をつけるのでなく、古いファイルのバックアップを取ってから上書き、という感じで使ってます。
ファイルが壊れる恐怖と日々戦うDTPerにとっては強い味方であります。けどMacAutomatorのほうが便利じゃね?

圧縮・解凍ツール

Cube ICE

いろんなの使ってきましたが、今はこれを使用中。MacのFinderデフォルトで圧縮されたzipファイルも文字化けせず解凍できます。
使用感はごく普通。標準的なアーカイブ形式にはだいたい対応してると思います。
圧縮時・解凍時に不要ファイルを任意でフィルタリングできるのが精神衛生上よいです。Thumbs.db爆発しろ。
インストール時に謎ツールバーをインストールするかどうか聞かれますので注意。

7-zip

普段使うことはありませんが、後述する比較ツールWinMergeアーカイブ比較機能を使うために入れています。

Explzh for Windows

ライセンスの関係で仕事用PCには入れてないですが、こちらもMacのデフォルトで圧縮したzipファイルを解凍できます。個人用PCではこちらを主に使っていますね。

ランチャー

CLaunch

パネル型のランチャー。有名どころだと思います。
タブ分けできるのである程度整理ができます。しかしだんだん登録アイテム数が増えてきてパネル自体が巨大に……
デスクトップのダブルクリックで呼び出すようにしているので、普段Adobeのアプリケーションとか使う時はあえて最大化せず、端っこ数ピクセルのスキマを作って(ダブルクリックできるようにして)います。

ファイラー(エクスプローラ拡張)

QTTabBar

Windowsエクスプローラをタブ型にするツール。たぶん似たようなのは山ほどありますが、最近は新しいのを開拓してないです。
いわゆるタブ型アプリケーションに求める機能はだいたい入っていると思います。動作のカスタマイズも細かくできます。
タブの横幅が可変で長いフォルダ名でも省略せず表示してくれること、タブをたくさん開いても多段表示できるので一覧性が損なわれないこと、あたりが個人的に好みですね。

データ比較ツール

WinMerge

定番中の定番。ファイルやフォルダを比較、テキストデータならその場でマージや編集もできます。
プラグインを入れればWordやExcel、PDFも比較可能。圧縮ファイルも解凍なしでそのまま中身を比較できます(要7-zipプラグイン)。
TortoiseSVNと連携させてリビジョン間の差分確認にも使っています。
InDesignのデータは直接比較できないけど、IDML書き出して拡張子をzipに変えたらWinMergeで修正箇所の割り出しができると最近気づきました。たまに便利。

ファイルバックアップ

BunBackup

定番。自動バックアップ、ミラーリング、フィルタ指定、上書き条件指定、世代管理などとりあえず欲しい機能が揃っています。これもカスタマイズ次第でかなり自分好みの設定を作れる。
作業データのバックアップ方法は今後変わりそうだけど、ちょっとしたデータのバックアップには使い続けるつもり。

Subversionクライアント

TortoiseSVN

最近使い始めたばかりのニューフェイス。作業データのバージョン管理を画策していまして、絶賛修行中なのです。
Windows用のGUIクライアントとしては定番どころか鉄板っぽい。エクスプローラを拡張するのはわかりやすいし便利なんだけど、ちょっと動作が重くなる気がしないでもない……
「とーたすえすぶいえぬ」、かるせど覚えた。

その他

WinCDEmu

CD/DVDのイメージファイル(isoとか)を開くと、空いているドライブレターが割り当てられ、通常のCD/DVDドライブと同じ感覚で扱うことができるようになる仮想ドライブツール。ドライブレターに空きがある限りいくつでも開けます。
なにも考えずイメージファイルをダブルクリックするだけで使えて、ドライブの解放も同じくダブルクリックでできます。仮想ドライブを右クリックして[取り出し]でもOK。このシンプルさが普段使いに便利。
例によって最初に出会ったのがこれだっただけで、他にもたくさんあるはず。

M電卓

http://www.vector.co.jp/magazine/softnews/080311/n0803113.html
電卓です。個人的には起動の速さと邪魔にならない小さいウィンドウが魅力かなあ。高機能らしいんだけど、関数電卓とか使わないし……
あと便利なのが16進数での演算。10進数で計算した結果がリアルタイムで16進数表示できたり、その逆もできたり。途中で切り替えもできます。16進数と10進数を混ぜて計算したいとき、たとえば文字コード連番でここから20文字分、とかによく使います。
ちなみに単位換算機能もあるのですが、ポイントがアメリカンポイントなので使ってないというオチ付き。

pinz

http://www.vector.co.jp/soft/win95/util/se343088.html
任意のウィンドウを最前面固定にしてくれるシンプルなツール。常駐型ですが邪魔にはならないです。
タスクバーから固定用アイコンをドラッグ→目的のウィンドウにドロップするだけで使えます。解除も同じく解除用アイコンをドロップだけでOK。
テキストエディタなりファイラーなり、それぞれに最前面固定機能は備えていると思いますが、私くらいになるといちいち個別に設定するのも(設定方法を覚えるのも)めんどくさいですからね。愛用してます。

などなど

思いつくままにいろいろ書いてみました。ほぼ毎日使うものだけに絞った*1のであんまり数なかったですね。
似た機能を持つフリーソフトがたくさんあって選択肢が多いのがWindows界のいいとこなので、「そういう使い方ならこっちのが便利だよ」というのがあったらぜひ教えてくださいー。

*1:諸事情で書かなかったやつもある

INDD 2013 Tokyo(spring)に行ってきた(第3セッション感想)

はいこんばんは。

先週の土曜日はInDesignの祭典、INDD 2013 Tokyo (spring)に行ってきました。セミナーのレポートとか最近サボり気味なんですが、今回は自分の業務にダイレクトにつながる内容だったので、せっかくだから俺は赤い扉を選感想など書いてみようと思います。他の2セッションについても書こうと思ったのですが力尽きましたごめんなさい……
感想なので、内容のまとめではありません。内容については当日のTwitterの様子がまとめられたTogetterがあるのでそちらを。
http://togetter.com/li/464817
うわっ……私のpost、多すぎ……?(例の顔で

第3セッション:多ページ作成でのInDesignテクニック

テクニカルドキュメント、いわゆる「トリセツ」を日々制作されている西村さんが、あかねさんを相棒に多ページのドキュメントを大量に処理する場合のテクニックを解説してくれるセッション。
立場や仕事量などもろもろ違いますが、私も主な業務は同じくテクニカルドキュメントの制作です。この種の作業の特徴としては、スライドで挙げられていた

  • ページ数が多い
  • ファイル数も多い
  • レイアウトはシンプル
  • テキストがメイン

に加え、

  • ほかのページへ誘導する記述が大量にある(X章のXを参照、XXページを参照、など)
  • ページ増減が頻繁にある(途中のページに記述がどっさり追加され、以降の改ページ位置などがすべて変わる)
  • 改訂が多く、データが長年引き継がれ続ける
  • 改訂サイクルが早い(ことがある)

などが挙げられると思います。
下版した次の日から改訂作業開始とかざらにあったり。逆に5年も前のデータを掘り起こしたり。最近は時代の流れか紙のトリセツも減りまして、最終的な出力がPDFでWeb掲載だったりすると、数百ページあるのに作業期間は1〜2日とかね。ありますね。
数百ページのドキュメントにちりばめられた「詳しくはXXページを参照してください」って記述を手作業で検索して参照元と引き合わせて違ってたら手で数値打ち直して……とか、ページ増減で内容とずれてしまった柱テキストを目視確認してちまちま修正して……とか、「テキスト修正だからそんなに時間かからないでしょ? PDFだし、できたらすぐちょうだい」、と、か……考えただけでSAN値*1ががりがり削れていきます。

そんな作業をInDesignで(正気を保ちつつ)効率的に行うためには、どんな機能をどう使ったらいいのか? というのが今回のセッション。
その内容ですが……いやー、濃かったですね! ものっすごいボリューム。

  • テキスト変数
  • 箇条書き
  • 相互参照
  • アンカー付きオブジェクト
  • ブック
  • 目次
  • 索引
  • (条件テキスト)

まさに普段業務で使っている機能ばかりで嬉しくなってしまいました。
逆に、初めて「この機能ってこういう時に使うのか」と知った人もいるんじゃないでしょうか? Adobeのヘルプではそれぞれ別の箇所に記述されていて、こんなふうに逆引き的に関連付けてまとまってはいないんですよね。機能の存在と概要を知らないと調べようがない。そもそも自動番号が箇条書きってどういうことなの……とかね。
セッションでは、取り上げたそれぞれの機能について、何がどう効率化できるのか、どう設定したら使えるのか、使うときの注意点は何か、を丁寧に説明していました。
この「注意点」のところが実はセッションの目玉だったんじゃないかと。ものすごく実践的で、「あるある!!」と何度も何度もうなずきながら聞いてたり、もちろん初めて知るものもたくさんあったり。単なる機能紹介や公式のヘルプでは絶対出てこないですよね、箇条書きの自動番号で51番目以降は丸数字が使えないとかw
設定方法についても、普段使ってる部分以外はあいまいにしか理解していなかったのできっちり復習できました。索引の参照形式とか、アンカー付きオブジェクトの位置オプションとか。後者はスライドで視覚的にまとめられていてわかりやすかったです。今まで回りくどいやり方をしていなかったか、チェックしてみるいい機会になりました。


実を言えば、これらの機能は日々使っているものの、他の環境ではどんなふうに使われているんだろう……といつも思っていたんですよね。よそで制作したデータを見ることが一切ないので。Webで検索してみても、実践的な内容はなかなか出てこないですし。
もっと効率的なやり方があるのではないか? むしろ根本的に機能を勘違いしていたりしないか? と不安を抱いていたのですが、今回ピンポイントでテクニカルドキュメントの例を見ることができ、おおむね方向性は間違っていないとわかって心底ほっとしました。そのテンションの結果があのpost数だよ*2


改めて今回紹介されていた機能を俯瞰してみると、何をおいてもまずは「ドキュメントを(データ上で)構造化すること」がはじめの一歩になるんだなあと思います。変数、相互参照、箇条書き、目次などなど、「テキストに適切なスタイルが設定されている」からこそ使えるんですよね。
逆にこれらの機能をフルに使いたいなら、それを前提にしたスタイルの設計をする、というのも必要になってくると思います。「見出しだから文字を大きくする段落スタイルを付けよう」ではなくて、「見出しとして使いたい段落だから見出しという段落スタイルを付けて、見た目でも見出しとわかるように文字を大きくしよう」という感じ。
データを構造化しておくと、さらに進んだ自動処理(たとえばスクリプトを使うとか)をしやすくなったり、Eなんとか形式にするときにもたぶん比較的スムーズだったりと、メリットは多いと思います。


あとひとつ、大事だと思ったのは、最後のまとめにあった「自動だからと安心しないでチェックは必ずしよう」です。
各機能の注意点を見返してみるとわかりますが、自動更新されたりされなかったり、テキスト量によってオブジェクトが重なってしまったり、自動挿入されるテキストが手動で修正できてしまったりと、落とし穴も結構あります。相互参照まわりとかちょいちょい怪しいし*3
自分の仕事では、修正作業に時間がかからない分、チェックは念入りに……というつもりでやっています。
終了まぎわに西村さんがぽそっと言ってた、「あくまで入力支援ですからね」という言葉を肝に銘じておきたいですね。

というわけで

InDesignかわいいよInDesign
横見出し機能搭載はよ
貴重な話を聞かせていただいてありがとうございました! 姐さん、よければ次は条件テキストも教えてください!

*1:正気度

*2:参照元と参照先を間違えてるところがあって赤面

*3:相互参照、便利でかわいいやつだけどダメなところもたくさん知ってます。こちらのBlogに詳しいです→ InDesignerの悪あがき ブログ内検索で「相互参照」を検索するといろいろ出てきます。