Hey, Scripting Guy!

Scripting Guys が皆さんの質問にお答えします

Hey, Scripting Guy!

TechNet コラムへようこそ。このコラムでは、よく寄せられるシステム管理スクリプトに関する質問に Scripting Guys がお答えします。システム管理スクリプトについて質問がある場合は、scripter@microsoft.com (英語のみ) までお送りください。すべての質問に回答することはできないかもしれませんが、可能な限り対応いたします。

詳細情報

Hey, Scripting Guy! カテゴリ別アーカイブ

Hey, Scripting Guy! 日付別アーカイブ

Hey, Scripting Guy! ダウンロード

Spacer

*

テキスト ファイルのすべての行の順序を逆にする方法はありますか

Hey, Scripting Guy! Question

Scripting Guy さん、よろしくお願いします。テキスト ファイルを 180 度 "逆転" する方法はありますか。ファイルの最後の行を最初の行に、ファイルの最初の行を最後の行にし、その間のすべての行についても同様に位置を入れ替える必要があります。

-- BJM

SpacerHey, Scripting Guy! AnswerScript Center

BJM さん、こんにちは。ほとんどの人は Scripting Guys について「確かに、この人たちはシステム管理スクリプトに関してはずば抜けた才能を持っているよね。でも、それは単に Scripting Guys がオタクの集団だってことさ。システム管理スクリプト "以外" についてどんな細かいことでも知っているような博識な人物ではないよ」と考えていることでしょう。本当のことをお教えしましょうか。本当のところ、Scripting Guys は・・・。何ですって。Scripting Guys がシステム管理スクリプトに関してはずば抜けた才能を持っているなんてだれが言ったのか、ですって。ほら、いつも青いシャツを着ているあの人ですよ。それに、緑の野球帽をかぶった女の子もです。彼女は私たちがずば抜けたなんとかだと言いました (彼女が叫んでいたこと全部を聞き取るのはちょっと難しかったのですが、多分 "ずば抜けた才能を持っている" と言っていたと思います)。それから、言うまでもありませんが、今日のコラムにはとても載せきれないほど多くの人々が言っています。

それくらい多くの人が言っているのです。

とにかく、私たちが言いたいのは・・・。ええと、ちょっと待ってください。私たちが言いたいのは・・・。少し待ってください。私たちが言いたいのは、Scripting Guys は本当に博識で、システム管理スクリプト以外のことについても知っているということです。たとえば、「テキスト ファイルの最後の行を最初の行に、テキスト ファイルの最初の行を最後の行にする方法」について、BJM さんから送られてきた質問を目にしたとき、私たちはすぐに "このように、後にいる者が先になり、先にいる者が後になる" というぶどう園のたとえ話が思い浮かびました。とても博識な感じがしますよね。

もちろん、私たちにはこの表現をどうすればよいのかよくわかりませんでしたし、このたとえ話をシステム管理スクリプトに関連付ける方法もわかりませんでした。つまり、私たちの知識は小さく断片的で、実用的な使い道がまったくありませんでした。でもそれが知識人の定義ではないとしたら、何を知識人というのかわかりませんね。

ですから、認めましょう。ぶどう園のたとえ話がテキスト ファイルの行を逆転することと、どう関係があるのか私たちにはわかりません (ただし、ときどきコラムに有名な引用句を挟み込めば、きっと自分たちが話していることをわかっているように聞こえるだろうとは思っています)。しかし、次のスクリプトの方が、テキスト ファイルのすべての行の順序を逆にする作業にもう少し関係があることはわかっています。

とにかく、はっきりとわかっています。スクリプトは次のとおりです。

Const ForReading = 1
Const ForWriting = 2

Dim arrLines()
i = 0

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)

Do Until objFile.AtEndOfStream
    Redim Preserve arrLines(i)
    arrLines(i) = objFile.ReadLine
    i = i + 1
Loop

objFile.Close

Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForWriting)

For i = Ubound(arrLines) to LBound(arrLines) Step -1
    objFile.WriteLine arrLines(i)
Next

objFile.Close

ご覧のように、まず、ファイルの行を "逆転する" テキスト ファイルを開くときに必要になる ForReading と ForWriting という 2 つの定数を定義します。2 つの定数を定義したら、次のコード行を使用して arrLines という名前の動的配列を宣言します。

Dim arrLines()

配列を宣言したら、カウンタ変数 i の値を 0 に設定します。

: 何ですって。"動的配列" をご存知ない方がいらっしゃるようですね。VBScript では、単純な従来の配列のサイズは固定されているので、前もって配列に割り当てられる最大の項目数を 10、12、15 などと指定する必要があります。これに対して、動的配列はもう少し動的です。配列はどのようなサイズにもなり、その場でサイズを変更して新しい項目を格納できます。ここで動的配列が必要な理由は単純です。この配列はテキスト ファイルから読み取ったすべての行を格納するのに使用しますが、実際にテキスト ファイルに何行あるかわからないので、配列のサイズを前もって指定できません。そのため、動的配列を使用するしかないのです。

次に、Scripting.FileSystemObject オブジェクトのインスタンスを作成し、OpenTextFile メソッドを使用して読み取り用に C:\Scripts\Test.txt ファイルを開きます。この処理を行うのが、次の 2 行のコードです。

Set objFSO = CreateObject("Scripting.FileSystemObject") 
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)

上記のコードを実行したら、スクリプトの次の部分に移ります。

Do Until objFile.AtEndOfStream 
    Redim Preserve arrLines(i) 
    arrLines(i) = objFile.ReadLine 
    i = i + 1 
Loop

ここでは、テキスト ファイルをすべて読み取り終えるまで (つまり、AtEndOfStream プロパティが True になるまで) 実行される Do Until ループを設定しています。ループ内での最初の処理として、次のコード行を実行します。

Redim Preserve arrLines(i)

実は、Redim Preserve ステートメントは動的配列のサイズを変更するための鍵です。Redim ステートメントは配列のサイズ変更に使用します。一方、Preserve ステートメントは、配列内の既存のデータを必ず維持するようスクリプトに指示します。ちなみに、これは非常に重要です。たとえば、Preserve ステートメントを省略して次のコード行を実行するとします。

Redim arrLines(i)

この場合、何が起きるでしょうか。このコード行を実行すると、配列のサイズが変更されます。しかし、配列の既存の項目は削除され、サイズが i の空の配列が残ります。

そういえば、カウンタ変数 i を使用して配列のサイズを示していることに注意してください。i の現在の値は 0 なので、ループ処理の 1 回目では、arrLines という名前の配列のサイズは 0 になります (つまり、配列で保持される項目は 1 つです。配列には、サイズが示す値よりも必ず 1 つ多い項目が保持されます。これは、配列の最初の項目には必ずインデックス番号 0 が与えられるためです)。

では、サイズが 0 の配列を作成したら、どうするのでしょうか。次は、ReadLine メソッドを使用してテキスト ファイルの 1 行目を読み取り、その値を配列の項目 0 に代入します。次に、この処理を行うコード行を示します。

arrLines(i) = objFile.ReadLine

その後、i の値を 1 増加します。つまり、ループ処理の 2 回目では、i の値は 1 になります。これはつまり、配列のサイズを 1 に変更し (配列で保持できる項目が 2 つになり)、テキスト ファイルの次の行を読み取ってその行の値を配列の 2 つ目の項目 (インデックス番号が 1 の項目) に代入するということです。この処理はテキスト ファイルのすべての行を読み取って処理するまで続きます。すべての行が処理されたら、Do Until ループを自動的に終了します。ループが終了したら、Close メソッドを使用して C:\Scripts\Test.txt ファイルを閉じます。

これはどういうことかというと、arrLines 配列には、テキスト ファイルの各行が含まれているということです。では、この配列には、次のような行が含まれているとしましょう。

E. This is line 5. 
D. This is line 4. 
C. This is line 3. 
B. This is line 2. 
A. This is line 1.

つまり、arrLines 配列は C:\Scripts\Test.txt ファイルとうり二つです。

これで、テキスト ファイルの行を逆転する準備ができました。実は、VBScript にはテキスト ファイルの行を逆転する関数がありません。しかし、VBScript には配列を最後から最初に向かって読み取るプロシージャがあります。この方法で、ファイルの行の順序を逆にします。

そのために、次の手順では C:\Scripts\Test.txt ファイルを今度は書き込み用として再び開きます。

Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForWriting)

ファイルを再び開いたら、次のコード ブロックを実行します。

For i = Ubound(arrLines) to LBound(arrLines) Step -1 
    objFile.WriteLine arrLines(i) 
Next

ここでは、配列の項目を最後から最初に向かって読み取っています。Ubound 項目 (配列の最後の項目) から開始して、Lbound 項目 (配列の最初の項目) で終了しています。つまり、For Next ループは項目 4 で始まり (今回のような項目が 5 つある配列では、最後の項目のインデックス番号は 4 です)、項目 0 で終了します (配列の最初の項目のインデックス番号は必ず 0 です)。Step -1 というステートメントを使用して、項目 4、項目 3、項目 2、項目 1、項目 0 の順に、ループが逆方向に実行されるようにします。

ループ内では、WriteLine メソッドを使用して現在の配列項目 (1 回目のループ処理では項目 4) の値をテキスト ファイルに書き込んでいるだけです。この処理は配列のすべての項目にアクセスするまで続きます。すべての項目にアクセスしたら、ファイルを閉じて、もっと知的な趣味に取り掛かります。

ESPN の「Baseball Tonight」の他の回を見るなどの知的な趣味です。

このスクリプトを実行すると、最終的にどうなるのでしょうか。最終的に、次のようになります。

A. This is line 1. 
B. This is line 2. 
C. This is line 3. 
D. This is line 4. 
E. This is line 5.

ご覧のとおり、テキスト ファイルの行を逆転することができました。最初の行が最後の行に、最後の行が最初の行になっていて、その間のすべての行も適切な位置に入れ替わっています。これはまさに期待通りの並べ替え方です。

BJM さん、おわかりいただけたでしょうか。ところで、他にもテキスト ファイルに関する質問があったそうですね。でも心配しないでください。そちらの質問については近いうちにお答えします。ただし今は、1 つはっきりさせておかなければならないことがあります。原典を再確認したところ、ぶどう園のたとえ話には "このように、後にいる者が先になり、先にいる者が後になる" という表現が載っていないことがわかりました。正しい解釈は、本当は "このように、後にいる者が先になり、Scripting Guys が後になる" です。もちろん、私たちはずっと前からこの表現を知っていました。

少なくとも、去年の勤務評定以来ずっとです。


ページのトップへページのトップへ