[JavaScript]String.matchとRegExp.execと後方参照
Adobeのサポートデータベース関係のぐりもんを書いたり添削してもらったりした結果、新たに覚えたことがあるので、せっかくだからメモっておきます。
以下だらだらと続くけど、実はMozilla Developer Center見れば全部書いてあるので、そっち見たほうがいいと思います。
match - MDC
exec - MDC
String.matchとRegExp.exec
String.matchとRegExp.execは、どちらも正規表現を使って文字列のマッチングを行うメソッド。
検索を行って、マッチした文字列を得たいときに使う。マッチするかどうか(と、マッチした位置)だけを調べたいときは、余分な処理をしないString.searchまたはRegExp.testを使ったほうが速い。
String.match(RegExp)とRegExp.exec(String)は、正規表現にgオプションがついてないときは同じ動作をする。
gオプションなしの場合の動作(String.match/RegExp.exec共通)
返り値は配列になる。配列の中身は
[0] : 最後にマッチした文字列全体 [1] : キャプチャされた部分文字列1番目(RegExp.$1と一致) [2] : キャプチャされた部分文字列2番目(RegExp.$2と一致) ・ ・ ・ [n] : キャプチャされた部分文字列n番目(RegExp.$nと一致)
capturing parentheses、キャプチャされるグループ? 後方参照? 言い方がいろいろあってよくわからない。とにかくマッチした文字列の中で、あとから部分的に取り出すことができるように保存する。
RegExpのプロパティだと$9までになるけど、この方法だと部分文字列の数に制限はないらしい。
マッチする文字列が無かった場合は、nullを返す。
今までこういう形で配列に格納されることを知らなかったので、いちいちRegExp.$1にアクセスしてたよ……。
var str = "ABCDEFGHIJKLMN"; var regexp = /([A-N])([A-N]{2})[A-N]/; var match_result = str.match(regexp); // ["ABCD", "A", "BC"] var exec_result = regexp.exec(str); // ["ABCD", "A", "BC"]
どちらも ["ABCD", "A", "BC"]の配列になる。
それぞれマッチング直後のRegExp.$1を調べると、どちらも"A"を返す。
つまり1つ目のグループの文字列を得たいときは、match_result[1]/exec_result[1]/RegExp.$1のどれかにアクセスすればおk。この場合は配列を変数に格納してるので、それを使ったほうがいい。
gオプションありの場合の動作(String.match)
返り値は、マッチしたすべての文字列全体の配列。と書くとたいへんわかりづらい……。カモン文章力。
要するに、部分文字列は配列に含まれないということ。
var str = "ABCDEFGHIJKLMN"; var regexp = /([A-N])([A-N]{2})[A-N]/g; var match_result = str.match(regexp);
マッチするたびに「次の検索開始位置」がregexp.lastIndexに格納され、マッチするものがなくなるまで続く。結果は ["ABCD", "EFGH", "IJKL"]の配列になる。
ただしもちろん部分文字列の保存は行われるので、直後にRegExp.$1を調べると"I"を返す(最後に行われたマッチングの分が格納されている)。
同じregexpオブジェクトを使ってマッチングを繰り返した場合、すべて同じ結果になる。
var str = "ABCDEFGHIJKLMN"; var regexp = /([A-N])([A-N]{2})[A-N]/g; var match_result1 = str.match(regexp); // ["ABCD", "EFGH", "IJKL"] var match_index1 = regexp.lastIndex; // 0 var match_result2 = str.match(regexp); // ["ABCD", "EFGH", "IJKL"] var match_index2 = regexp.lastIndex; // 0 var match_result3 = str.match(regexp); // ["ABCD", "EFGH", "IJKL"] var match_index3 = regexp.lastIndex; // 0
マッチに失敗した場合はnullが返る。
gオプションありの場合の動作(Regexp.exec)
返り値は、最初にマッチした文字列全体+キャプチャした文字列の配列。ただし、一度実行すると、正規表現オブジェクトに「次回検索開始位置」が設定される(lastIndexプロパティ)。
なので、同じオブジェクトでマッチングを繰り返すと、そのたびに返り値は変わる。
var str = "ABCDEFGHIJKLMN"; var regexp = /([A-N])([A-N]{2})[A-N]/g; var exec_result1 = regexp.exec(str); // ["ABCD", "A", "BC"] var exec_index1 = regexp.lastIndex; // 4 var exec_result2 = regexp.exec(str); // ["EFGH", "E", "FG"] var exec_index2 = regexp.lastIndex; // 8 var exec_result3 = regexp.exec(str); // ["IJKL", "I", "JK"] var exec_index3 = regexp.lastIndex; // 12
lastIndexだけ更新して、あとはgフラグないときと同じように動くって考えればいいのかな。
マッチに失敗したらlastIndexを0に戻して、nullを返す。
これを利用すると、こんな風に処理を繰り返せる。
var str = "ABCDEFGHIJKLMN"; var regexp = /([A-N])([A-N]{2})[A-N]/g; var m = []; while (m = regexp.exec(str)) { alert(m[2]); }
「BC」「FG」「JK」と表示されます。
……もうちょっと実用的な例にするべきだった気がする。
で、つまり
何が言いたかったかというと、こないだのぐりもんのここの部分(11〜13行目)。
最新版じゃないけど気にしない。
11: var m = location.search.match(/^\?(\d{6})\+/); 12: if (!m) return; 13: var doc_number = m[1];
これはCodeReposの方でdrry氏が書きなおしてくれた部分(の一部)。その前はこう書いてた↓
11: location.href.match(/\?(\d{6})\+/); 12: var doc_number = RegExp.$1;
修正版を見たときどうしてこう書けるのかわからなくて、上記のことを調べてやっと理解したのでした。
String.matchが「マッチした文字列+キャプチャした文字列」の配列を返すことを利用して、RegExp.$1へのアクセスを省略。加えて、マッチしなかった場合はnullを返すので、文書番号が6桁のパターンに当てはまらなかった場合はその時点で処理を中断する、というやり方。
たいへん勉強になりました。
隅っこでこっそりとはいえ、何か書いて人に見てもらえるっていいことだなあというお話でした。
Adobeサポートデータベースの旧URLから新URLにジャンプするGreasemonkey
最終更新日:2008/03/13
旧URLにアクセスすると30秒後には問答無用で新検索画面に転送されるようになってしまった。
お知らせ:
アドビシステムズでは、サポート情報検索ページを新しいページに一新いたしました。以前ご利用いただいていたサポート文書のURLにアクセスした場合、一旦こちらのページへ案内されます。下記のリンクより新しい検索画面に移動し、その検索ページから検索機能をご利用ください。
文書番号は変わんないのに全部検索画面トップへ。
……なんじゃあそりゃあああ!!
文書番号がわかれば 検索画面に入力→検索でひっかかる って手順が使えるけど、このご時世ありえないのでわ……
というわけで
むしゃくしゃしてぐりもん書いた。反省などするわけがない。
でも見よう見まねなので間違ってたらごめんなさい。Firefox+Greasemonkey入れてる人しか使えなくてごめんなさい。
追記:添削してもらいました。最初に自分で書いたのも記録としてここに残しておきたいので、添削後の最新版についてはこの記事の一番下を参照してください。 ↓最新版へ
// ==UserScript== // @name Adobe FAQ Redirector // @namespace http://d.hatena.ne.jp/chalcedony_htn/ // @description Redirector for Adobe Support Database (Japanese) // @include http://www.adobe.com/jp/support/announcement/* // @include http://support.adobe.co.jp/faq/faq/* // ==/UserScript== (function() { var this_url = location.href; this_url.match(/\?(\d+)\+/); var doc_number = RegExp.$1; var new_url = "http://www.adobe.com/jp/support/kb/ts/" + doc_number.substr(0,3) + "/ts_" + doc_number + "_ja-jp.html" if(location.href != new_url) { location.href = new_url; } })();
Windows版Firefox3.0.6、Greasemonkey0.8.20090123.1で確認。
今はhttp://support.adobe.co.jp/faq/faq/(ほにゃらら)にアクセスするとhttp://www.adobe.com/jp/support/announcement/(ほにゃらら)にリダイレクトされるけど、一応古いのも対象に入れておいた。
こういうときRedirectって使っていいんだっけ?
ちなみに、URLの仕様は
たとえばこんなのを見たいとして
[227421]Illustrator 8.0 以前で作成した EPS ファイルを InDesign 文書に配置するとフォントが他の書体に置き換わる
旧URLはこれ。
http://support.adobe.co.jp/faq/faq/qadoc.sv?227421+002
http://www.adobe.com/jp/support/announcement/?227421+002(現在のリダイレクト先)
新URLはこうなる。
http://www.adobe.com/jp/support/kb/ts/227/ts_227421_ja-jp.html
数字の上3桁でなにやら分けているみたい……ていうか、前の002ってなんだったんだろ。
あ、追記
うpした直後にこんな情報を発見。
新アドビサポート情報のドキュメントへのショートカット - DTP+印刷営業メモ
新URLへの転送用URL、よりシンプルなのがあったのね。
こっちにすればよかったかな。でもあんまり飛んだり跳ねたりするのもなんだしな。
さらに追記
Greasemonkey特有のなにかを使ったりはしていないので、OperaとかChromeとかでも似たようなこと出来るんじゃないかと思った。
さらにさらに追記
ArcCosine先生が添削してくれました。あざーす!!
現在のurlを調べて分岐してる部分は@exclude使えば消せる。それから、文書番号取り出したいだけなら今のurlを変数に入れておく必要もなかったですね。
この程度だったら、if文とかいらないし、変数も減らせるしって感じで。
もうひとつ、location.hrefに代入すると履歴が残ってしまうので、location.replace()で移動するように変更されてます。なるほどー。
CodeReposにもうpしてもらいました。↓飛んだ先に最新版インストール用のリンクがあります。
http://coderepos.org/share/browser/lang/javascript/userscripts/adobe_faq_redirector.user.js
夜中にテンション上がりました。Web楽しいなー。
さらにさらにさらに追記(2009.03.09)
不具合があったので修正しました。
ver1.1だと、http://www.adobe.com/jp/support/announcement/ のトップなど文書番号が含まれてない場合でもジャンプしようとしてしまいます。なので、FAQ(TechNoteって名前なのかな……)のパターンにマッチするときだけ飛ぶように変更。
// ==UserScript== // @name Adobe FAQ Redirector // @namespace http://d.hatena.ne.jp/chalcedony_htn/ // @description Redirector for Adobe Support Database (Japanese) // @include http://www.adobe.com/jp/support/announcement/* // @include http://support.adobe.co.jp/faq/faq/* // @exclude http://www.adobe.com/jp/kb/* // @version 1.2 // ==/UserScript== (function() { if(location.href.match(/\?(\d{6})\+/)) { var doc_number = RegExp.$1; location.replace("http://www.adobe.com/jp/support/kb/ts/" + doc_number.substr(0,3) + "/ts_" + doc_number + "_ja-jp.html"); } })();
ついでに、文書番号が6桁かどうかもちゃんと見ることにしました。
Firefox3で期待通り動くのを確認。
CodeReposには代理うpしてもらったので、どうするかちょっと悩む。commit権とやらをもらって頑張る機会なのかもしれないけど、なにしろ見よう見まねもおぼつかないくらいだからね……。
さらにさらにさらにさらに追記(2009.03.10)
CodeReposのほうで再び添削してもらいました! ありがたや。
改めて最新版へのリンクを張っておきます。
http://coderepos.org/share/browser/lang/javascript/userscripts/adobe_faq_redirector.user.js
Cooooool!!
サポートデータベースには実は2種類? あって、文書番号が6桁の技術文書と4桁のサポート文書に分かれてます。
このぐりもんで対応してるのは6桁のほう。4桁のほうも同時に対応したいところなんだけど、リダイレクトされる先のURLに文書番号が含まれないなどでちょいと困った。どうすべきか考え中です。
もし今度修正したときは別エントリにしますw
あと、いろいろ添削してもらって新たに覚えたことがあるので、それもあとで改めて書こうと思います。
アウトプットしてみてよかったなあ。としみじみ。
さらに×5追記(2009.03.13)(最新版はここから!)
史上最高ブクマ数*1を更新したので、別エントリにするのも何だなと思い悩んだあげくにこのまま追記します。文書番号6桁、4桁、3桁のURLに対応しました。
// ==UserScript== // @name Adobe FAQ Redirector // @namespace http://d.hatena.ne.jp/chalcedony_htn/ // @description Redirector for Adobe Support Database (Japanese) // @include http://support.adobe.co.jp/faq/faq/*?*+* // @include http://www.adobe.com/jp/support/announcement/?*+* // @exclude http://www.adobe.com/jp/kb/* // @version 1.3 // ==/UserScript== (function() { var m = location.search.match(/^\?(\d+)\+/); if (!m) return; var doc_number = m[1]; switch (doc_number.length) { case 6: location.replace("http://www.adobe.com/jp/support/kb/ts/" + doc_number.substr(0, 3) + "/ts_" + doc_number + "_ja-jp.html"); break; case 4: location.replace("http://www.adobe.com/jp/support/kb/cs/" + doc_number.substr(0, 1) + "/cs_" + doc_number + "_ja-jp.html"); break; case 3: location.replace("http://www.adobe.com/jp/support/kb/cs/" + "000" + "/cs_" + doc_number + "_ja-jp.html"); break; } })();
たぶん、これで最後。
http://support.adobe.co.jp/faq/faq/qadoc.sv?7318+001 → http://www.adobe.com/jp/support/kb/cs/7/cs_7318_ja-jp.html
http://support.adobe.co.jp/faq/faq/qadoc.sv?262+001 → http://www.adobe.com/jp/support/kb/cs/000/cs_262_ja-jp.html
という感じになるみたいです。……なんだこの000は。
★CodeReposのほうにも反映してもらいました(ArcCosine師匠ありがとう)。こちらもご利用ください。
http://coderepos.org/share/browser/lang/javascript/userscripts/adobe_faq_redirector.user.js
*1:6だけど……
InDesignで見開きからはじまるドキュメントを作る
InDesignで普通にドキュメントを作ると、右綴じなら奇数ノンブルが左、左綴じなら奇数ノンブルが右ページになります。最初のページが奇数ノンブルだったら単独ページ(見開きでなく片方だけのページ)になる。当然ですね。
なので見開きからはじめたい場合は最初のページのページ番号を偶数にすればOKなのですが、あえて奇数ノンブルからはじめたい場合はちょっとややこしい。
というわけで、いつもわかんなくなるので自分用にメモ。以下、メニューの名称等はWindows版InDesign CSに基づきます。
見開きページではじめたい場合(偶数ノンブルはじまり)
- 普通にドキュメントを作成する
- ページパレットで先頭ページを選択し、メニューの [ ページ番号とセクションの設定 ] で [ ページ番号割り当てを開始 ] にチェックして偶数を入力する
見開きページではじめた新規ドキュメントを作りたい場合(奇数ノンブルはじまり)
- 実際のページ数 + 1ページでドキュメントを作成する
- ページパレットメニューの [ ページを移動許可 ] を「オフ」にする
- 1ページ目を削除する
作業途中で見開きに変更したくなった場合(奇数ノンブルはじまり)
ええ、なぜかあるんですこういうことが。。*1
- ページパレットメニューの [ ページを移動許可 ] を「オン」にしておく
- ドキュメントの先頭に1ページ挿入する
- [ ページを移動許可 ] を「オフ」に戻す
- さっき挿入した1ページ目を削除する
移動許可をオフにした状態でページを足しても、単独ページが増えるだけで見開きになってくれません。デフォルトではオンになってるはずだけど、いじっちゃってたときはオンに戻してからやること。
[ スプレッド分離禁止 ] ?
ページの移動を制限する設定としては、同じページパレットメニューの [ スプレッド分離禁止 ] というのもあります。
要するにこれも現在のページ配置を固定するものなんですが、ドキュメント全体の設定である [ ページを移動許可 ] に対して、こちらは選択したページのみ固定します。
複雑なものを作りたい場合はこっちを使うのがいいけど、単純に見開きにしたいだけだと結局全スプレッドを分離禁止にすることになるので、ちょっとめんどくさいかも。
参考
ていうか、ここにまとめて書いてあるんですけどね。しかも丁寧にたっぷり画像つきで。
InDesignの勉強部屋_CS3_見開きから始める
*1:むしろこれを書きたいがためのエントリだったです
iGoogle便利だけどタブとかいらないよ派
なんです。表示されるのはいつも同じアイテムで十分。(使うのがニュースと単語翻訳くらいなものなので……)
なのに一つしかないタブ(ホーム)の部分で幅を取られるので、スタイルシートで消してしまっています。
以下自分メモ
#col1 { display:none !important; } #c_2 .modbox { margin-right:0px !important; } .rnd_footer .rnd3 { margin-left:0px !important; } .rnd_footer .rnd2 { margin-left:1px !important; } .rnd_footer .rnd1 { margin-left:3px !important; }
iGoogleの設定で消せたりするのかなあ……見当たらないんだけど。
たまには自分のことなど
たまには自分のことを書こうかなと思いました。
職種としてはDTPオペレータで、中規模ソフトハウスの自社製品マニュアルを作っています。社内の印刷関係の仕事がいろいろ回ってくるので、たまにしょっぱいデザインもやらかします。何年やってもへなちょこです。
プログラミングは、興味はあっても体系的に勉強したことは一度もありません。見よう見まねですね。
無知と無恥を晒しながらでもブログを書こうと思ったのは、メモ程度であっても人の目を意識して書いたほうが勉強になると思ったからです。
当面の目標は、人にツッコミを入れてもらえる程度にはまともなものを書くこと。
基本的にクローズドな自分にしては大冒険です。
プログラムのようなものを書いていていつも思うのは、「これ、ほんとのプログラム書く人から見たら酷いものなんだろうな……」ってことです。
セオリーとか効率とか、まるでわかりません。本を読んだりWeb上の解説を読んだりはしても、所詮は見よう見まねです。ごめんなさい。何に謝っているのかわかりません。
それでも、「プログラムは『やりたいことを、今よりちょっと便利にやる』ためのもの」と信じて、今はとにかく自分が期待したように動くものをどんどん書こうと考えています。
プログラミングは素人でも、自分の需要については自分が一番詳しいはず。……とか言ってみたり。
DTPのほうも勉強して、他の人の需要にも詳しくなろう。
Webには便利なものが無数に、無造作に転がっていて、無償で(!)公開されているスクリプトを仕事でも大いに使わせてもらっています。いつもありがとうございます。
いつかは自分も、誰かの役に立てるものを作れるようになって恩返しをしたいです。
本題
ええと、なんか上では語ってますが、私の本性はただのネットジャンキーなド変態ですよ。
ちなみにハンドルは「かるせどにー」と読みます。htnはおまけ。
Illustratorの [ 効果 ] - [ 矢印にする ] で線幅が細いときに怪しい
作業中にたまたま気づいたことをメモっておく。Windows XPでしか確認してないです。
Illustratorで線のオブジェクトから [ 効果 ] - [ 矢印にする ] で矢印を作成するとき、もとのオブジェクトの線幅が細い(たぶん1pt以下)だと、先端のところで塗りの色が出てしまう。
[ 矢印にする ] メニューのところで大きさを変えても同様。
で、ここからが問題。Illustrator 10で塗りを「なし」にすると……
なぜか、塗りの色が出てた部分が白くなる。
印刷してもPDFにしても白い。[ アピアランスを分割 ] すると、元の線の色の三角形の背後に「塗り:白」の三角形が誕生。
Windows XP SP2、Illustrator 10.0.3で確認。Illutrator CSではちゃんと透明になっていたので、不具合なのかな。いや、普通に考えて意図しない動作なんだけども。
解決方法
ぐぐったら出てきた。
効果メニューの矢印の特徴 めもブロ。*1
普通に線のオブジェクトを選択して [ 効果 ] を適用すると、「線」と「塗り」の両方に適用される。
塗りの色が出るのはそのせいなので、適用後にアピアランスパレットで効果の適用範囲が「線」だけになるように指定するか、はじめから同パレットで「線」だけを選択した状態で効果を適用するかすれば、塗りの色が出なくなる(もちろん謎の白い部分も出現しなくなる)。
というわけで
あとから縮小とかする可能性を考えると、[ 効果 ] メニューから矢印を作りたいときは、線幅にかかわらず「線だけに適用する」って覚えておいたほうがいいかも。
*1:関係ないけど、このブログのデザイン可愛いな。ヘッダにいる生き物がまばたきするー(*´∀`)