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 さん、よろしくお願いします。検索単語の一覧を含むテキスト ファイルがあります。別のファイルでこれらの各単語を検索して、見つからないすべての単語を返すスクリプトを記述したいと思っています。このスクリプトを記述する方法を教えてください。

-- PH

SpacerHey, Scripting Guy! AnswerScript Center

PH さん、こんにちは。このコラムを執筆している Scripting Guy にとって今日は良い日です。これは少し驚きです。というのも、事態を把握するまで、今日は危うくとても嫌な日になるところだったのですから。

このコラムを執筆している Scripting Guy は、昨夜仕事からの帰宅途中に、自分と息子用に 1 つずづ、2 つの特大サンドイッチを買おうとレストランに立ち寄りました。サンドイッチ 2 つ分の代金を支払う際、「店内でお召し上がりですか。それともお持ち帰りですか」とレジ係の女性が彼にたずねました。

店内ですって。このコラムを執筆している Scripting Guy は特大のサンドイッチを 2 つ注文したとき、そのときレストランにいた客は彼だけでした。一瞬、Scripting Guy は衝撃を受けました。レジ係の女性が、彼のことを 2 つの特大サンドイッチを 1 人でむさぼるように食べる男だと思ったのは明らかでした。しかも平然と、そのようなことを聞いたのです。それは間違いだと言ってください。大食は 7 つの大罪の 1 つなのですから。

: 興味がある方のために付け加えておきますと、他の大罪には色欲、強欲、怠惰、憤怒、嫉妬、傲慢があります。驚いたことに、編集者はすべての大罪を犯しています。

このコラムを執筆している Scripting Guy が事態を把握したとき、彼は、もう少しでレストランを飛び出すところで、(少なくとも、またサンドイッチが食べたくなるまでは) 決して来店しないつもりでした。しかし、レジ係の女性は、彼のことを大食のだらしない男だとはまったく思っていませんでした。それどころか、胃が小さく、鳥のように食が細い、このかわいそうな Scripting Guy が栄養失調でないかと心配していました。彼女は Scripting Guy のようにやせた男性は数ポンド体重を増やした方がいいと思ったので、彼にサンドイッチを 2 つとも食べてほしいと思っていました。このコラムを執筆している Scripting Guy はこのことに気付くと、ただ微笑んで彼女にチップを渡し、店を後にしました。

: このコラムでドーナツに関するトピックを扱った回数を考えると、このコラムを執筆している Scripting Guy が、機械のようにスクリプトを作成する、やせてみすぼらしい風貌の人物だとは信じがたいかもしれません。それは当然です。でも、編集者に聞いてみてください。たとえば、彼女は先日、このコラムを執筆している Scripting Guy がタコサラダを食べたのを見ましたが、タコスの皮は 1 口も食べませんでした。おわかりでしょうか。鳥のように食の細い持ち主なのです。

編集者注 : 体重 28 g のノドアカハチドリは、1 日に体重のほぼ 2 倍にあたる 50 g の食料をとることをご存知でしょうか。ですから、このコラムを執筆している Scripting Guy の食欲が鳥のようであるという意見には賛成です。

でも心配しないでください。このコラムを執筆している Scripting Guy はやせているかもしれませんが、栄養失調ではありません。何しろ、栄養失調だったら、次のようなスクリプトを記述する力を奮い起こすことはできないでしょう。次のスクリプトでは、あるテキスト ファイルから単語を取得し、別のテキスト ファイルでそれらの各単語を検索します。

Const ForReading = 1 
 
Set objFSO = CreateObject("Scripting.FileSystemObject") 
Set objFile = objFSO.OpenTextFile("C:\Scripts\SearchText.txt", ForReading) 
 
strContents = objFile.ReadAll 
objFile.Close 
 
Set objFile = objFSO.OpenTextFile("C:\Scripts\Terms.txt", ForReading) 
 
strSearchTerms = objFile.ReadAll 
objFile.Close 
 
arrSearchTerms = Split(strSearchTerms, vbCrLf) 
 
For Each strItem in arrSearchTerms 
    intFound = InStr(strContents, strItem) 
    If intFound = 0 Then 
        Wscript.Echo "The search term " & strItem & " was not found." 
    End If 
Next

このスクリプトのしくみを説明しましょう。まず、ForReading という名前の定数を定義してから、この定数に値 1 を代入しています。この定数は、テキスト ファイルを開くたびに使用します (2 つのテキスト ファイルを使用して作業しますが、これは大食だからではありません。2 つのテキスト ファイルを操作するように PH さんに頼まれたからです)。定数を定義したら、次のように Scripting.FileSystemObject のインスタンスを作成し、OpenTextFile メソッドを使用して C:\Scripts\SearchText.txt ファイルを読み取り用として開きます (ちなみに、このファイルは検索するファイルです)。

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

その後、ReadAll メソッドを呼び出してテキスト ファイル全体の内容を読み取り、strContents という名前の変数にその情報を格納します。もちろん、この操作によって安全にメモリに格納されたファイルの仮想コピーが作成されることになります。これは好都合です。というのも、ここでは、テキスト ファイル自体を検索するのではなく、この仮想コピーを検索するからです (まさにそうする必要があります)。ですから、Close メソッドを呼び出し、SearchText.txt ファイルを閉じます。

おわかりいただけましたか。それは良かったです。ご参考までに、SearchText.txt の内容 (つまり strContents 変数の値) は次のようになります。

'Curiouser and curiouser!' cried Alice (she was so much surprised, that  
for the moment she quite forgot how to speak good English); 'now I'm  
opening out like the largest telescope that ever was! Good-bye, feet!'  
(for when she looked down at her feet, they seemed to be almost out of  
sight, they were getting so far off). 'Oh, my poor little feet, I wonder  
who will put on your shoes and stockings for you now, dears? I'm sure  
_I_ shan't be able! I shall be a great deal too far off to trouble myself  
about you: you must manage the best way you can; --but I must be kind to  
them,' thought Alice, 'or perhaps they won't walk the way I want to go!   
Let me see: I'll give them a new pair of boots every Christmas.'

: 不思議の国のアリスからの抜粋でしょうか。おかしなことに、これは、このコラムを執筆している Scripting Guy のイベント ログに記録されてしまう種類のイベントです。

次に、もう一度だけ OpenTextFile を呼び出します。今回は、(検索する単語を含む) C:\Scripts\Terms.txt ファイルを開きます。その後、ReadAll メソッドをもう一度呼び出します。今度は、次のように strSearchTerms という名前の変数に抽出されたデータを格納します。

strSearchTerms = objFile.ReadAll

次に、Close メソッドを呼び出し、Terms.txt ファイルを閉じます。

つまり、この時点で、strSearchTerms 変数の値は次のようになります (Terms.txt ファイルから読み取ったデータを反映しています)。

boots 
hat 
pants 
shirt 
shoes 
telescope

この値は適切でしょうか。各項目を個別に検索する必要があるので、あまり適切ではありません (つまり、boots という単語を検索してから、hat を検索し、次に pants という単語を検索する必要があります。それ以降の単語についても同様です)。つまり、どうにかしてこの一覧を個々の検索単語に分ける必要があるということです。

これを行うのが次のコード行です。

arrSearchTerms = Split(strSearchTerms, vbCrLf)

ここでは、Split 関数を使用して、strSearchTerms 変数に格納されている文字列値を arrSearchTerms という名前の配列に変換しているだけです。復帰改行文字 (vbCrLf) で分割すると、次の項目で構成される配列が作成されます。

boots

hat

pants

shirt

shoes

telescope

「おや、検索する単語のような項目だな」とお考えなら、それは正解です。配列の各項目は、検索する単語のいずれかと一致します。すごいですよね。

当然、このように検索単語が準備できたのに、実際にそれらの単語を検索しないというのはあまりおもしろくないですよね。これを行うのが次のコード ブロックです。

For Each strItem in arrSearchTerms 
    intFound = InStr(strContents, strItem) 
    If intFound = 0 Then 
        Wscript.Echo "The search term " & strItem & " was not found." 
    End If 
Next

ここでは、arrSearchTerms 配列内の各項目をループ処理する For Each ループを設定しています。検索単語ごとに、InStr 関数を使用して、その値がテキスト ファイル (厳密には、strContents 変数の値) に見つかるかどうかを確認します。これを行うのが次のコード行です。

intFound = InStr(strContents, strItem)

検索単語が見つかった場合、InStr 関数では、文字列内のその単語の開始位置に対応する整数を返します。検索単語が見つからない場合、InStr 関数からは 0 が返されます。ファイル内に見つからなかった単語を返すにはどうしたらよいでしょうか。それは簡単です。次のように、intFound 変数の値 (文字の開始位置) が 0 であるかどうかを確認するだけです。

If intFound = 0 Then

intFound 変数の値が 0 以外の場合、それが意味することはたった 1 つです。つまり、検索単語が strContents 変数の値に含まれているということです。したがって、0 以外の値が返された場合は、ループ処理の先頭に戻り、次の単語について同じ処理を繰り返します。intFound 変数の値が 0 の場合も、結論は 1 つしかありません。つまり、検索単語が strContents 変数の値に含まれていないということです。この場合、次のように、当該の単語が見つからなかったことを示すメッセージをエコー バックします。

Wscript.Echo "The search term " & strItem & " was not found."

このコラムのために用意したサンプルのテキスト ファイルと検索単語を使用すると、次のようなレポートが返されます。

The search term pants was not found. 
The search term shirt was not found.

配列のすべての項目のループ処理が完了したら、作業完了です。仕方がありませんね。皆さんも仕事を切り上げていいですよ。今日はもう帰宅してもらってかまいません。皆さんの上司が 1 日分の給料を差し引くと言ったら、Scripting Guys に請求書を送付してください。お支払いいたします。

: 編集者は今日少し疲れているので、彼女に代わって次のような注釈を付け加えようと思います。「このことについてよく考えましたが、やはり Scripting Guys には請求書を送らないでください。また、皆さんの上司が許可しても、まだ帰宅すべきではありません。机に向かって仕事を続けてください。悪魔の手は暇人に忍び寄るということを覚えておいてください。」

だれもが編集者のことを敬愛している理由がおわかりでしょう。彼女がいると本当におもしろいんです。

補足しておきます。このコラムを執筆している Scripting Guy が帰宅して、袋からサンドイッチを取り出すと、レジ係の女性が 253,000 枚ものナプキンを入れておいてくれたことがわかりました。どうやら、息子がだらしない食べ方をすると思ったようです。

: Scripting Guy の息子はだらしない食べ方をするのか、ですって。いいえ、むしろピラニアに近い食べ方をします。食後、食べ残しや食べかすは、どこにもありません。

言われてみれば、そのおかげで洗い物が楽です。


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