Hey, Scripting Guy!

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

Hey, Scripting Guy!

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

詳細情報

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

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

Hey, Scripting Guy! ダウンロード

Spacer

*

テキスト ファイルから 2 つのタグで囲まれたすべてのテキストを抽出する方法はありますか

Hey, Scripting Guy! Question

Scripting Guy さん、よろしくお願いします。1 つのフォルダ内に膨大な数のテキスト ファイルがあります。これらの各ファイルを開き、<filecount> タグと </filecount> タグで囲まれたすべてのテキストを抽出して、その情報を別のファイルに書き込む必要があります。どうすればいいでしょうか。

-- RP

SpacerHey, Scripting Guy! AnswerScript Center

RP さん、こんにちは。先日、このコラムを執筆している Scripting Guy と息子は、TechEd IT Forum 2007 に参加するために今度バルセロナに出張することについて話していました。Scripting Guy の息子は、「まだわからないんだけど、どうしてみんなはお父さんと話がしたいの」と言いました。

父親である Scripting Guy は、「それはね、父さんがちょっとした有名人だからだよ」と言いました。

それを聞いた息子は「それは違うよ、有名人だったら、ウィキペディアに載っているはずだもん」と言いました。

ええ、息子の言うとおりです。Scripting Guys が有名だったら、ウィキペディアに Scripting Guys のページがあるはずです。ですが、そのようなページはありません。これは、Scripting Guys は自分たちが思っているほど有名でも偉くもないということでしょうか。

もちろん、そんなことはありません。Scripting Guys は謙虚で控え目なので、だれかがウィキペディアに書き込めるほど私生活を公開していないというだけのことです。確かに、自分たち自身でウィキペディアに書き込むこともできますが、それはあまり正当な行為とは思えません。ですから、Scripting Guys の項目をウィキペディアに載せることに興味がある方のために、選りすぐりの逸話をいくつかご紹介しましょう。

1949 年、Scripting Guy の Jean Ross は、ミス アイオワ美人コンテストで 3 位に選ばれました。しかし、1950 年のすばらしい作品『Dairy Farm Scandal』(酪農場のスキャンダル) で演じた役柄が原因で、数か月後にはその肩書きを手放すことを余儀なくされました。

1606 年 2 月、ガリレオ ガリレイが若い弟子であった Scripting Guy の Peter Costantini に、Peter はコンピュータで実行中のプロセスを監視するのに役立つスクリプトを使用できると話しました。まさにその日、Peter は、コンピュータで実行中のプロセスを監視する方法についての最初のコラムを執筆しました。現在、スクリプト センターでは、その最初のコラムだけでなく、Peter がその後執筆した同じテーマに関する 347,286 本のコラムも掲載しています。

2005 年 8 月、Scripting Guy の Dean Tsaltas は自分でサンドイッチを作りました。「とてもおいしかった」と Dean は思い出しています。「サブウェイやクイズノスのものには劣るけど、とてもおいしかった」と。

2007 年 10 月、Scripting Guy の Greg Stemp は「有名人だったら、ウィキペディアに載っているはずだもん」と言われました。

これだけの情報があれば、ウィキペディアの編集部が Scripting Guys の項目を許可してくれることでしょう。でも、許可されない場合は、ウィキペディアの編集部が、テキスト ファイルから 2 つのタグで囲まれた情報を抽出できるスクリプトがあることに気付くまでお待ちください。まず、1 つのファイルでこの処理を実行するコードをご紹介しましょう。その後、フォルダに含まれるファイルごとに、この情報を抽出できるさらに手の込んだスクリプトをご紹介します。

基本のスクリプトは次のとおりです。

Const ForReading = 1 
 
Set objFSO = CreateObject("Scripting.FileSystemObject") 
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading) 
 
strContents = objFile.ReadAll 
objFile.Close 
 
strStartText = "<filecount>" 
strEndText = "</filecount>" 
 
intStart = InStr(strContents, strStartText) 
intStart = intStart + Len(strStartText) 
 
intEnd = InStr(strContents, strEndText) 
 
intCharacters = intEnd - intStart 
 
strText = Mid(strContents, intStart, intCharacters) 
 
Wscript.Echo strText

確かに、おっしゃるとおりです。このコードは、難解であるように見えますね。でも、心配しないでください。このコラムを読み終えるまでころには、その混乱が解消されることをお約束します。

: "混乱を解消する" (de-gibberish) というのは専門用語です。本当に有名で、オンライン百科事典への掲載に値する人だけが使用できる表現です。

しかも、正しく使用します。

まず、ForReading という名前の定数を定義してから、その値を 1 に設定します。この定数は、テキスト ファイルを開く際に使用します。次に、Scripting.FileSystemObject のインスタンスを作成し、OpenTextFile メソッドを使用して C:\Scripts\Test.txt ファイルを読み取り用として開きます。そのコードを次に示します。

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

では、この開いたファイルで何を行うのでしょうか。実のところ、たいした処理は行いません。ReadAll メソッドを呼び出して内容を読み取り、strContents という名前の変数に格納した後、Close メソッドを呼び出してすぐにファイルを閉じるだけです。

ファイルの内容がメモリに格納されたら、そのファイルは必要なくなるので、心配無用です。いいですか、信じていいんですよ。Scripting Guys が皆さんの期待を裏切ったことはありますか。

: 皆さんの期待を裏切ったという内容はウィキペディアの項目に記載しないことをお勧めします。何しろ、ウィキペディアでさえ、記事の長さについてなんらかの制限を設けているでしょうから。

RP さんは、<filecount> タグと </filecount> タグで囲まれたすべてのテキストを抽出する必要があります。RP さんのテキスト ファイルがどのようなものかはっきりとはわからないので、サンプル ファイルとして次のファイルを使用します。

<filename>Test.txt</filename> 
<filedate>10/23/2007</filedate> 
<filecount>786</filecount> 
<filelocation>C:\Scripts</filelocation>

この対象の 2 つのタグは、各ファイルで 1 度だけしか使用されないことを前提としている、ということをお伝えしておきます。これらのタグが複数回使用される場合、このスクリプトは機能するのか、ですって。すみませんが、機能しません。ただし、近い将来、補足のコラムを執筆して、その方法をご紹介します。

また、このスクリプトは、ファイルの記述方法に関係なく機能する優れものだということも付け加えておきます。次のようなファイルはどうでしょうか。

<filename>Test.txt</filename><filedate>10/23/2007 
</filedate><filecount>786</filecount><filelocation> 
C:\Scripts</filelocation>

大丈夫です。このファイルでも、スクリプトを使用して対象のタグで囲まれたテキストを抽出できます。また、次のようなテキストの場合も可能です。

<filename>Test.txt</filename><filedate>10/23/2007 
</filedate><filecount>786 
</filecount><filelocation>C:\Scripts</filelocation>

それから・・・。ええ、もうおわかりですね。

ともあれ、次は、strStartText と strEndText という名前の 2 つの変数に 2 つのタグを代入します。

strStartText = "<filecount>" 
strEndText = "</filecount>"

次は、このコード ブロックです。

intStart = InStr(strContents, strStartText) 
intStart = intStart + Len(strStartText)

上記のコードではどのような処理が行われているのか、ですって。1 行目では、InStr 関数を使用して <filecount> タグが始まる文字位置を判断しています。サンプルのテキスト ファイルでは、この文字位置は 65 になります。2 行目では、この値に <filecount> タグの長さ (つまり、タグを構成する文字数) を加算しています。タグの長さは 11 です。この値を 65 に加算すると、intStart 変数の最終的な値は 76 になります。これは、対象のテキストが文字位置 76 から始まるということでもあります。

少し難しいと感じた場合は、次の表を参照してみてください。この表では、65 から 75 までの文字位置を詳しく示しました。ご覧のとおり、<filecount> タグが文字位置 65 から始まる場合は、文字位置 75 で終了するということになります。

65

66

67

68

69

70

71

72

73

74

75

<

f

i

l

e

c

o

u

n

t

>

要するに、抽出するテキストは文字位置 76 から始まるということになります。

次に、次のコード行を使用して、2 つ目のタグ (</filecount>) の開始位置を判断します。

intEnd = InStr(strContents, strEndText)

サンプル ファイルでは、この文字位置は 79 になります。

求める結果に近づいてきました。次は、抽出する必要がある文字数を判断します。この値は、終了タグ (</filecount>) の開始位置から抽出するテキストの開始位置 (76) を引いて計算できます。

intCharacters = intEnd - intStart

この計算結果はどうなるでしょう。79 から 76 を引くと、その答えは 3 になります。この 3 という値は 2 つのタグで囲まれた文字数です。

この値がわかったら、次のように Mid 関数を呼び出し、2 つのタグで囲まれた文字を抽出できます。

strText = Mid(strContents, intStart, intCharacters)

ここでは、strContents 変数の値を取得し、文字位置 76 (intStart 変数の値) を開始位置として、3 文字分 (intCharacters 変数の値) 移動し、移動しながら各文字を取得するようにスクリプトに指示しているだけです。一体どのような値が取得されるのかというと、次の値です。

786

すばらしいですよね。

では、詳しい説明は抜きにして、フォルダ内のすべてのテキスト ファイルに対して、この処理を実行し、取得したファイル数を Totals.txt という名前のファイルに書き込むスクリプトをご紹介します。

Const ForReading = 1 
 
Set objFSO = CreateObject("Scripting.FileSystemObject") 
strComputer = "." 
 
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 
 
Set colFiles = objWMIService.ExecQuery _ 
    ("ASSOCIATORS OF {Win32_Directory.Name='C:\Scripts'} Where " _ 
        & "ResultClass = CIM_DataFile") 
 
For Each objFile In colFiles 
    Set objFile = objFSO.OpenTextFile(objFile.Name, ForReading) 
 
    strContents = objFile.ReadAll 
    objFile.Close 
 
    strStartText = "<filecount>" 
    strEndText = "</filecount>" 
 
    intStart = InStr(strContents, strStartText) 
    intStart = intStart + Len(strStartText) 
 
    intEnd = InStr(strContents, strEndText) 
 
    intCharacters = intEnd - intStart 
    strCount =  Mid(strContents, intStart, intCharacters) 
 
    strText = strtext & strCount & vbCrLf 
Next 
 
Set objFile = objFSO.CreateTextFile("C:\Scripts\Totals.txt") 
 
objFile.Write strText 
objFile.Close

これで本当にうまくいくのか、ですって。ご自分で試して結果をご覧になってください。

Scripting Guys に関する新しいウィキペディアの項目を急いで作成しようとしている方にお伝えしておくと、ウィキペディアでは "まったくでたらめな内容" の書き込みが禁止されています。ウィキペディアの考慮すべきガイドラインによると、"まったくでたらめな内容" には次のようなものが含まれます。

1.

完全に無意味なもの。例えば、全く意味の通らない文。酔って書いたりするとこうなりがちです。あるいはファンレターや日記や特定企業への問い合わせや抗議のような文。例「○○くん、いつも応援しています。これをよんだらメールをください」

2.

一見すると、どうにか意味が通っているようにみえる駄文。しかし、どんなに頭のよい人がよく読んだとしても全体像がつかめないくらい、内容が完全にわけのわからない、修正不能なもの。

皆さんにとってこれはどういうことかと言うと、「Scripting Guys の項目は書いていただいてかまいませんが、Hey, Scripting Guy! のコラムは転載しないでください」ということです。

: ご参考までに、Scripting Guys はウィキペディアを告訴するつもりはありません。"一見すると、どうにか意味が通っているようにみえる駄文。しかし、どんなに頭のよい人がよく読んだとしても全体像がつかめないくらい、内容が完全にわけのわからない、修正不能なもの。" という部分が、明らかにスクリプト センター スタイル ガイドからそのまま引用したものであってもです。そのうえ、入力している文字が何であるかをまったく気にせずキーボードのキーを押してコラムを執筆することは、このコラムを執筆している Scripting Guy の専売特許です。

でも、別にいいんですよ。成り行きにまかせましょう。

編集者注 : 1949 年にはとうてい生まれていない編集者は、このコラムを定期的に読んでいる何百万人もの熱心な Hey, Scripting Guy! ファンのために (わかりましたよ、"何百万" というのは少し大げさかもしれませんが、数名の熱心な読者がいるのはわかっています)、話を完結させなければいけないと思いました。

先週、このコラムを執筆している Scripting Guy はワシントン対オレゴンのフットボール ゲームの話をしましたが、今週は話題にしませんでした。もう十分話しましたので。

このコラムを執筆している Scripting Guy は、ワールド シリーズの優勝チームを予測しました。また、人前で馬鹿にするような発言もしました。ボストン レッド ソックス ファンの皆さん、好きなだけあざ笑ってください。


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