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 さん、よろしくお願いします。テキスト ファイルの行を結合する方法はありますか。

-- JN

SpacerHey, Scripting Guy! AnswerScript Center

JN さん、こんにちは。テキスト ファイルの行を結合する方法をお知りになりたいのですね。実を言うと、今日はちょっと急いでいます。このコラムを執筆している Scripting Guy は、サン ピエトロ寺院とシスティーナ礼拝堂に行く準備をしているところなのです。でも、こうしましょう。出かけるまで、まだ少し時間があるので、できる範囲でご説明します。

:聞かれる前にお答えしますが、昨年の勤務評定の結果を受け取る直前の時期に、このコラムを執筆している Scripting Guy がバチカン市国を訪れているのは単なる偶然です。とは言うものの、個人的な利益を求めて祈りを捧げることに反対はしているわけではありません。念のため。ただ、今回の勤務評定が前回と同じような場合は、言うまでもないことですが、このコラムを執筆している Scripting Guy には、たとえ神の力添えがあっても、どうしようもなさそうです。

ああそうでした。テキスト ファイルの行と行を結合するんでしたね。いったいどうすれば・・・。あっ、待ってください。いい考えが浮かびました。旅のすばらしさの 1 つは、有名な人物について、あまり知られていない事実に触れる機会が得られることです。たとえば、ミケランジェロの場合を例にご説明しましょう。ミケランジェロがサン ピエトロ寺院のドームの設計に携わり、ピエタ像を彫り、システィーナ礼拝堂の天井画を描いたことは、有名です。ただし、ミケランジェロは芸術家としての才能が開花する以前に、見習いとしてスクリプトを記述していたということは、ほとんど知られていません。これは、すごくすてきなことです。当時、彼が記述したスクリプトの 1 つが公開されていて、テキスト ファイルの行を結合するという事実は、さらにすてきなことです。このコラムを執筆している Scripting Guy はバチカン図書館で手書きのスクリプトのコピーを入手したので、テキスト ファイルの行を結合する問題に対するミケランジェロの解決法をご紹介しましょう。

Const ForReading = 1
Const ForWriting = 2

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

Do Until objFile.AtEndOfStream
    strLine1 = objFile.ReadLine
    strLine2 = objFile.ReadLine
    strNewLine = strLine1 & strLine2
    strNewContents = strNewContents & strNewLine & vbCrLf
Loop

objFile.Close

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

システィーナ礼拝堂が、ミケランジェロの最高傑作と考える人もいます。しかし、このコラムを執筆している Scripting Guy は、このテキスト ファイルの行を結合するコードを初めて見たときに感極まって涙を流したことを認めますし、そのことを恥ずかしいとも思っていません。美しいです。美しいの一言に尽きます。

ミケランジェロが記述したコードのしくみを説明する前に、少し時間を取って JN さんの状況を詳しく見てみましょう。JN さんのテキスト ファイルは次のような状態です。

SID123456
789
SID234567
891
SID345678
912
SID456789
123

これは本当に短くてすてきなテキスト ファイルですね。ただし、残念なことに形式が整っていません。各行の末尾の 3 文字がどういうわけか、次の行に配置されています。このテキスト ファイルを次のような状態にするというのが、JN さんの希望です。

SID123456789
SID234567891
SID345678912
SID456789123

つまり、この例では、テキストは 8 行ではなく 4 行にしたいということです。

おや、浮かない顔つきなのはなぜですか。いいですか、がっかりすることはありません。これからご覧に入れますが、この壊れたテキスト ファイルを、単に修正するだけではなく、それを最小の労力で成し遂げるのです。

さて、JN さんのテキスト ファイルをどうやって修正しましょうか。ミケランジェロは、ForReading および ForWriting という 2 つの定数を定義して問題を解決しました。おそらくご存知でしょうが、これらの 2 つの定数はテキスト ファイルを開く際に使用します (そして、これもご存知でしょうが、このファイルは 2 回開く必要があります。1 回目は既存のファイルの内容を読み込むため、2 回目は変更したファイルの内容を書き出すためにファイルを開きます)。この 2 つの定数を定義したら、次のように Scripting.FileSystemObject オブジェクトのインスタンスを作成し、次のコードを使用して、読み取りのために C:\Scripts\Test.txt ファイルを開きます。

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

次のタスクは、ファイルの既存の内容を 1 行ずつ読み込むことです。それには、まず、テキスト ファイルの終わりに達するまで (つまり、ファイルの AtEndOfStream プロパティが True になるまで) 実行する Do Until ループを設定します。このループでは、ReadLine メソッドを使用してファイルの 1 行目を読み取り、その値を strLine1 という名前の変数に格納します。

strLine1 = objFile.ReadLine

そして直ちに ReadLine メソッドを再び呼び出し、ファイルの 2 行目を読み取り、その値を strLine2 という名前の変数に格納します。

strLine2 = objFile.ReadLine

この処理の目的は何でしょうか。現在、strLine1 変数の値は SID123456 で、strLine2 変数の値は 789 です。これらの 2 つの値を結合したら結果はどうなるでしょう。そう、当たりです。次の値になります。

SID123456789

説明するまでもなく、これがまさに、必要としていた値です。これを踏まえて、次のコード行を使用して 2 つの値を結合し、新しい文字列を strNewLine という名前の変数に格納します。

strNewLine = strLine1 & strLine2

これでファイルの 1 行目を新しく書き換えられます。そこで、次のコード行を使用して、strNewContents 変数の値に新しい行 (および改行文字) を追加します。

strNewContents = strNewContents & strNewLine & vbCrLf

その後、ループ処理の先頭に戻り、この処理を繰り返し、元のテキスト ファイルの 3 行目と 4 行目のデータを結合して、その値を同じように strContents 変数に追加します。このループ処理が完了すると、strContents 変数の値は次のようになります。

SID123456789
SID234567891
SID345678912
SID456789123

この時点で、Close メソッドを呼び出してテキスト ファイルを閉じ、今度は書き込みのために、そのファイルをすぐに再度開きます。

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

ファイルを開いたら、Write メソッドを使用して既存の Test.txt の内容を strNewContents 変数の値で置き換えます。

objFile.Write strNewContents

そして、最後にファイルを閉じて完了です。やはり、真の天才ですね。

: このスクリプトはファイルの行数が偶数行の場合のみ有効であることをお知らせしておきます。JN さんから問い合わせがあった件では、ファイルの行数が常に偶数行であるということでしたので。ただし、たまたまファイルの末尾に余分な行があると、このスクリプトはクラッシュして、次のメッセージが表示されます。

C:\scripts\hey0814.vbs(9, 5) Microsoft VBScript runtime error: Input past end of file

次のようにスクリプトの中でテキスト ファイルの 2 行目を読み込む前にそのファイルの末尾をチェックして、このエラーを回避できます。

Const ForReading = 1
Const ForWriting = 2

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

Do Until objFile.AtEndOfStream
    strLine1 = objFile.ReadLine
    strLine2 = ""
    If Not objFile.AtEndOfStream Then
        strLine2 = objFile.ReadLine
    End If
    strNewLine = strLine1 & strLine2
    strNewContents = strNewContents & strNewLine & vbCrLf
Loop

objFile.Close

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

Do Until ループの中に If ステートメントを追加したことに注目してください。strLine1 変数を読み込んでから strLine2 変数の内容をクリアし、最後に読み込んだ行がファイルの末尾の行かどうかを確認します。もしそうであれば、単純に次の ReadLine メソッドの呼び出しをスキップして新しい行 (strNewLine) を作成し、strLine1 変数と空の strLine2 変数の内容を strNewLine 変数に書き込みます。まだファイルの末尾に達していなければ、strLine2 変数の値を読み込みます。

ここでおそらくお知らせしなければならないのは、公平に見て、このコラムを執筆している Scripting Guy は、ミケランジェロに関しては常識と思われるレベルの知識さえ持ち合わせていないということです (ただ彼もさすがに、少なくとも今はということですが、ここで話題にしたミケランジェロは「ミュータント タートルズ」のキャラクターとは違うことはわかっています)。また、彼はミケランジェロが美の追求者でありながら、自分自身にはかまわない人だったことも知っています。ミケランジェロは粗末な服を身にまとい、食事はさらに粗末で、入浴もろくにしませんでした (本当に、まったくしませんでした)。ミケランジェロは少しばかり・・・変わり者でもありました。ある逸話によれば、モーセ像を完成させたミケランジェロは、「なぜおまえは私に話しかけてこないのだ」と、金づちで彫像のひざを砕いたとか。うーん・・・。

とにかく、ミケランジェロはうつ状態になることも多かったようです。彼は晩年に、次のような手記を残しています。

「私はひとりぼっちでみじめに生きていて、樹皮の内側の組織のように、身動きがとれない。死がすぐに訪れて私を救ってくれないのならば、あわれな年寄りは打ちのめされて、無になるまですり減っていくのだろう。痛みは私を惑わせ、引き裂き、そして打ち砕いた。死こそ私を待つ唯一の憩いの場所である」

これは、このコラムを執筆している Scripting Guy の職場でのようすを絵に描いたような文章です。


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