Peter Torr
Microsoft Corporation
December 13, 1999
日本語版最終更新日 2000 年 11 月 21 日
Microsoft Windows Script 5.5 の最終リリースにより、Microsoft は先進的な ECMA-262 (ECMAScript) の実装を提供するという約束を果たしました。バージョン 5.5 の Windows Script は、既存のスクリプト コードとの互換性を保つために必要ないくつかの違いを除いて、バージョン 3 の ECMAScript に準拠しています。ECMAScript バージョン 3 の変更点は、言語そのものの根本的な改善ではなく、もっぱら組み込みオブジェクトの拡張に関するものです。この記事では、JScriptR と Visual Basic® Scripting Edition (VBScript) 5.5 のいくつかの新機能を簡単に紹介します。
正規表現
正規表現は、あらゆる種類のテキスト データの検索と操作に利用できるきわめて強力なツールです。JScript はバージョン 3 から正規表現をサポートしてきました。今回は、パターン構文と replace メソッドの両方に新しい機能が追加されています。
置換関数の使用方法
これまで、文字列内のテキストを置換したいと思ったときには、2 つの選択肢がありました。
- 置換後のテキストが静的なテキストである場合には、1 行のコードで正規表現を使うことで、グローバル置換を 1 回行えば済みました。これはきわめて簡単な方法です。
- 置換後のテキストが「動的」なテキストである場合には、ループ内で indexOf または charCodeAt を呼び出して、置換文字列を手動で作成しなくてはなりませんでした。これは面倒です。
JScript 5.5 では、置換関数によって、この 2 つの方式の利点を同時に享受することができます。正規表現を使って、簡単な 1 行のグローバルな検索置換操作を実行する際に、置換文字列の代わりに Function オブジェクトを渡すことができるのです。この関数は正規表現にマッチするものが見つかるたびに呼び出されるので、「スマート」 な判断を行い、置換文字列を返す前に計算を実行することができます。
いくぶん複雑に聞こえるかもしれませんので、具体的な例を示しましょう。読者の多くは「ROT13 エンコーディング」という言葉を耳にしたことがあるでしょう。基本的に、ROT13 は、メッセージの内容を通りすがりのユーザーから隠すために使われる、弱い形でのテキスト エンコーディングの一種です。たとえば、ROT13 を使うと、news:rec.arts.movies newsgroup の映画のレビューの中の「ネタばれ」の部分を隠すことができます。コンピュータに Outlook Express がインストールされていれば、ニュースグループを読むときに、[メッセージ] メニューの [解読] メニュー項目を使って、この機能が動作している様子を見ることができます。
ROT13 という名前は、これが入力テキスト内のすべてのアルファベット文字を 13 文字分だけ「ローテート」することから来ています。もちろん、アルファベットには 26 種類の文字があるので、メッセージに対して ROT13 エンコーディングを 2 回適用すると、もとのメッセージが得られます。JScript 5.0 で独自の ROT13 エンコーダを作成するためには、文字列中のすべての文字をチェックし、1 文字ごとに置換を行うという古くからの charCodeAt のトリックを使用せざるをえませんでした。次にその例を示します。
// 大文字に ROT13 エンコーディングを適用する単純な関数
function OldRot13(s)
{
var sResult = "";
var i = 0;
var d = 0;
// 文字列中のすべての文字をチェックする
for (i = 0; i < s.length; i++)
{
// 次の文字を取得する
d = s.charCodeAt(i);
// 大文字か
if ((d >= 65) && (d <= 90))
{
// インクリメントする
d += 13;
// オーバーフローをローテートする
if (d > 90)
{
d = 64 + (d - 90);
}
}
// 文字を文字列に追加する
sResult += String.fromCharCode(d);
}
// 結果を返す
return sResult;
}
エンコーディングを行う実際のロジック (13 を追加し、オーバーフローをローテートする) が、外側のループのコードのせいでわかりにくくなっています。もちろん、文字ごとのローテーション コードをカプセル化するために、新たな関数を追加することもできますが、この汚いループ コードを消すことはできません。次にその例を示します。
var s = "A SECRET MESSAGE! ";
// エンコードする
var sEncoded = OldRot13(s);
window.alert(sEncoded);
// デコードする
s = OldRot13(sEncoded);
window.alert(s);
このコードの出力は、エンコード済みの文字列 "N FRPERG ZRFFNTR!" の後に、元の文字列 "A SECRET MESSAGE!" を続けたものになります。
ここまでは問題はないでしょう。汚いエンコーディング関数になりましたが、問題なく使うことができます。しかし、大文字以外の文字をエンコードしたい、たとえば特定のパターンにマッチする文字列だけをエンコードしたい場合を考えてみましょう。このためには、OldRot13 関数に手を入れる必要があります。あまりきれいな解決方法ではありません。
JScript 5.5 は、このような問題を解決してくれます。新しい関数置換機能を使うと、JScript 5.5 では OldRot13 と同じ機能を次のように実装することができます。
// Simple function to ROT 13 encode upper-case letters
function Rot13(sMatchedString)
{
// 文字コードを取得する
var d = sMatchedString.charCodeAt(0);
// インクリメントする
d += 13;
// オーバーフローをローテートする
if (d > 90)
{
d = 64 + (d - 90);
}
return String.fromCharCode(d);
}
使い方の例を示します (前のサンプルと同じ結果が得られます)。
// 大文字だけをエンコードする
var re = /[A-Z]/g;
var s = "A SECRET MESSAGE!";
// エンコードする
var sEncoded = s.replace(re, Rot13);
window.alert(sEncoded);
// デコードする
s = sEncoded.replace(re, Rot13);
window.alert(s);
いくつかの違いが目につきます。第 1 に、グローバルな置換関数のおかげでループのロジックが不要になったために、コードの量が半減しています。第 2 に、検索する文字のタイプを正規表現を使って指定しているので、変更が簡単になりました。母音だけをエンコードしたい場合には、最初の行を次のように変えればよいのです。
// 大文字の母音だけをエンコードする
var re = /[AEIOU]/g;
成功です ! "N SRCRRT MRSSNGR!" という文字列が表示されます (ただし、デコードのフェーズは動作しなくなっています。正規表現は依然として母音を探しているが、エンコードのために母音がすべてなくなっているからです)。
置換関数の構文
上の例では、置換関数には、sMatchedString という 1 つのパラメータだけが渡されていますが、この関数は実際には次のように可変個の引数を受け取ることができます。
replaceFunc(matchedString [, subMatch1 [, ...]] , matchPos, source)
|
パラメータ
|
値
|
|
matchedString
|
正規表現にマッチした部分文字列全体
|
|
subMatch1
|
最初に格納されたサブマッチ
|
|
...
|
それ以降に格納されたサブマッチ
|
|
matchPos
|
ソース文字列内の、マッチが発見された位置
|
|
source
|
検索対象となるソース文字列全体
|
置換関数には、少なくとも 3 つのパラメータ (matchedString、matchPos、および source) が渡されますが、正規表現に格納を行うためのサブマッチが含まれている場合には、追加のパラメータが渡されます。サブマッチは括弧を使って指定されます。たとえば、次のコードは、置換関数にサブマッチを渡す方法を示しています。
// 単語の後に 10 進数が続いているものを探す
var re = /(\w+)\s*(\d+\.\d+)/g;
// 検索対象のサンプル文字列
var s = "JScript 5.5 and VBScript 5.5";
s = s.replace(re, showParams);
window.alert(s);
function showParams(matchedString, subMatch1, subMatch2, matchPos, source)
{
var s = "Found a match!" +
"\n\tMatch = " + matchedString +
"\n\tSub-match 1 = " + subMatch1 +
"\n\tSub-match 2 = " + subMatch2 +
"\n\tMatch position = " + matchPos +
"\n\tSource string = " + source;
window.alert(s);
return (subMatch1 + " is great");
}
上のコードは、次の 3 つのアラート ダイアログを表示します。
Found a match!
Match = JScript 5.5
Sub-match 1 = JScript
Sub-match 2 = 5.5
Match position = 0
Source string = JScript 5.5 and VBScript 5.5
Found a match!
Match = VBScript 5.5
Sub-match 1 = VBScript
Sub-match 2 = 5.5
Match position = 16
Source string = JScript 5.5 and VBScript 5.5
JScript is great! and VBScript is great!
ドル記号変数
しかし、利点はこれだけには留まりません ! 置換関数だけでなく、置換文字列のための新しい置換トークンも多数追加されているのです。これらは Perl 5 から拝借したものなので、多くの人にはすでにおなじみのものでしょう。これらのトークンは、String.prototype.replace への呼び出しの置換文字列の中で使用することができますが、置換関数の中では使用できません。まあこれは仕方がないでしょう。
|
トークン
|
意味
|
|
$$
|
リテラル文字の '$'
|
|
$&
|
マッチした文字列全体
|
|
$`
|
マッチした部分文字列の前にある部分文字列
|
|
$'
|
マッチした部分文字列の後にある部分文字列
|
|
$nn
|
nn 番目に格納されたサブマッチ
|
|
$+
|
最後にマッチした部分文字列
|
|
$_
|
入力文字列全体
|
書式設定関数
JScript のニュースグループでよく発せられる質問の 1 つが、「小数点以下 2 桁になるように、数値を書式設定するにはどうすればいいのか」というものです。VBScript では、FormatNumber 関数を使って簡単に実現することができました。しかし、JScript のユーザーは、この単純な目的を果たすために、 Math.ceil と Math.pow を使った複雑な計算を行わなくてはなりませんでした。JScript 5.5 は、Number プロトタイプ オブジェクトの 3 つの新しいメソッド、toFixed(fractionDigits)、toExponential(fractionDigits)、および toPrecision(precision) を使ってこの問題を解決しています。最初の 2 つのメソッドは、小数点以下が fractionDigits 桁になるように数値を書式設定します。toFixed は通常の 10 進表記を使用し、toExponential は指数表記を使用します。最後のメソッド toPrecision は、必要ならば指数表記を使って、有効桁数が precision 桁になるように数値を書式設定します。
次の単純なスクリプトは、これらのメソッドと、改善された toLocaleString
(後述) の使い方を示しています。
var x = 1234.56789;
window.alert(x.toString());
window.alert(x.toLocaleString());
window.alert(x.toFixed(3));
window.alert(x.toExponential(3));
window.alert(x.toPrecision(3));
私のコンピュータ上では、このスクリプトは次の結果を表示します (コントロール パネルの地域設定で数値に対して異なる書式を設定している場合には、結果が変わることがあります)。
1234.56789
1,234.57
1234.568
1.235e+3
1.23e+3
toLocaleString
われわれによく寄せられるもう 1 つの要求が、日付を正しく書式設定する機能が欲しいというものです。Date.prototype.toString が生成する文字列は、歴史的な理由から、日付をユーザーに対して表示する目的には理想的なものとはいえません。このため、多くのスクリプト作成者は、独自のルーチンを作成しています。
JScript 5.5 のリリースに伴い、われわれはユーザーのコントロール パネルから書式情報を取得するように、Date、Number、および Array オブジェクトの toLocaleString をアップデートしました。Date オブジェクトの場合、戻り値は、ユーザーの Long Date 書式を使って書式設定されます。Number オブジェクトと値の場合は、小数点と千単位の区切り文字が使用されます。Array オブジェクトでは、個々の要素の連結にリスト区切り文字が使用されます。
これにより、ユーザーはようやく日付、数値、およびリストを自分の好みの書式で見られるようになったわけですが、これらの関数の戻り値を計算に使用することもできなくなりました。toLocaleString は、ユーザーに対する情報の表示のみに使用し、内部の計算の一部としては使用しないようにしてください。
その他
このリリースでは、JScript にこれ以外にもたくさんの機能が追加されています。残念ながら、ここではそのすべてを紹介することはできません。以下に、その他の機能のいくつかを簡単に示します。
- 新しい in 演算子は、オブジェクト (またはそのプロトタイプ チェーン) が、指定されたプロパティを含んでいるかどうかをチェックします。
if (prop in obj) // obj (またはそのプロトタイプ) が、
//prop という名前のプロパティを持っている
- Object プロトタイプ オブジェクトの新しいメソッド:
obj.hasOwnProperty(prop) // obj が prop という名前のプロパティを持っている
obj.
propertyIsEnumerable(prop) // prop は for..in ループで列挙可能である
obj.isPrototypeOf(proto) // obj がプロトタイプ チェーンの中に proto を持っている
- URI エンコーディングおよびデコーディング関数 (escape と unescape を使わなくて済むようになります)
encodeURI(text) // 文字列を完全な URI としてエンコードする
encodeURIComponent(text) // 文字列を URI コンポーネントとしてエンコードする
decodeURI(uri) // 完全な URI をデコードする
decodeURIComponent(uricomponent) // URI コンポーネントをデコードする
-
Array オブジェクトの新しいメソッドと改善されたメソッド (pop、push、shift、unshift など)
- 例外処理の finally ブロック (実際には JScript 5.0 にも存在していましたが、ドキュメントには記載されていませんでした)
VBScript について
このリリースでは、VBScript ユーザーのことも忘れているわけではありません。これまで、VBScript の正規表現のサポートは JScript のサポートほど完全なものではありませんでしたが、VBScript 5.5 ではようやくそのギャップが埋まりました。VBScript (および VBScript.RegExp COM コンポーネント) には、サブマッチと置換関数を含む、JScript の正規表現のすべての機能が含まれています。
VBScript におけるサブマッチ
VBScript 5.5 は、JScript と同じように格納を行うための括弧 (サブマッチ) をサポートするようになりました。この新しい機能は、Execute メソッドが返す個々の Match オブジェクトごとに SubMatches コレクションを用意することによって実装されています。次に、電子メール アドレスのコンポーネントを取得するための単純な例を示します (ただし、これは電子メール アドレスをマッチングするための完全な正規表現ではありません)。
Dim s, re, matches, match
s = "Please send feedback to msscript@microsoft.com"
Set re = New RegExp
' 電子メール アドレスをマッチングして、3 つの部分を格納
するための単純なパターン = "(\w+)@(\w+)\.(\w+)"
Set matches = re.Execute(s)
' マッチは 1 つのみ存在し、コレクションは 0 から始まる
Set match = matches(0)
' マッチ全体を表示する
window.alert("Complete match is " & match)
' 最初のマッチにどれだけのサブマッチがあったかを表示する
window.alert("Number of captures is " & match.SubMatches.Count)
' 個々のサブマッチを表示する
window.alert("Name is " & match.SubMatches(0))
window.alert("Organisation is " & match.SubMatches(1))
window.alert("Root domain is " & match.SubMatches(2))
結果として、以下のメッセージが表示されます。
Complete match is msscript@microsoft.com
Number of captures is 3
Name is msscript
Organization is microsoft
Root domain is com
VBScript 5.5 では、 MatchCollection コレクションと SubMatches コレクションはどちらも 0 をベースにしています。また、MatchCollection オブジェクトと SubMatch オブジェクトのデフォルト プロパティは Item です。
VBScript の置換関数
VBScript の置換関数は JScript とほぼ同じように動作します。単に、置換文字列の代わりに、 GetRef 関数の戻り値を渡します。
' 単純な置換関数。VBScript では、使用されない場合でも、
' 関数のすべてのパラメータを指定しなくてはならないことに注意。
Function Test(match, pos, source)
Test = "matched '" & match & "' at position " & pos & "..."
End Function
Dim s, re
' 単純なテストをセットアップする
s = "Hello there"
Set re = New RegExp
re.Pattern = "\w+"
re.Global = True
' Replace メソッドを呼び出し、結果を表示する
s = re.Replace(s, GetRef("Test"))
window.alert(s)
このコードは、文字列 "matched 'Hello' at position 0... matched 'there' at position 6... " を表示します。
(VBScript ではなく) Visual Basic での置換関数の使用は、Visual Basic が GetRef 関数をサポートしていないため、いくぶん複雑になります。パブリック デフォルト関数を持つ Class モジュールを作成し、 Replace メソッドにそのオブジェクトの新しいインスタンスを渡す必要があります。Visual Basic 6.0 で、関数をクラスのデフォルト メンバにするには、[ツール] メニューの [プロシージャ属性] を選択し、[詳細]ボタンをクリックし、[プロシージャ ID] コントロールを " (既定値) " に変更します。RegExp オブジェクトは、単にデフォルトとして登録されているメソッドを呼び出すので、関数の名前は何でもかまいません。クラスの作成とその属性の設定の詳細については、Visual Basic のドキュメントを参照してください。
関連情報とフィードバック
Windows Script 5.5 のアップデートされたドキュメンテーションが Windows Script Web サイトで公開されています。
このリリースに関するフィードバックは、Windows Script チーム (msscript@microsoft.com) かパブリック ニュースグループ (news://msnews.microsoft.com/microsoft.public.scripting.jscript) にお送りください。Windows Script 5.5 の新機能に関する (簡単な) 一覧が JScript のニュースグループにあり、Deja.com にアーカイブされています (http://x40.deja.com/getdoc.xp?AN=555513203
)。
Windows Script 5.5 の新しい機能が読者の役に立つことを祈っています !
Peter Torr は、オーストラリア出身の Microsoft の Windows Script 担当プログラム マネージャで、一日中、ドイツのトランス ミュージックを聴いています。ひまなときには、映画を見たり、「アメリカ英語」に腹を立てたり、映画を見たり、アメリカの食べ物に文句をつけたり、映画を見たりしています。