†DOSとバッチ†

文字列処理2

作成開始日 2018.06.08
最終更新日 2018.06.09

文字列中から部分文字列を「検索」する処理を考える。

たとえば、「エントロピーから始める熱力学第12回.m4a」という文字列から、回数情報(ここでは「12」)を取り出す場合を考える。要するにタイトル自体は不要で、回数のみが必要な場合を想定している。もちろん、同一タイトルで回数のみ違う場合には、回数位置を決め打ちにして切り出してもよいが、それでは流石に汎用性が低すぎる。タイトルが違っても「(タイトル)第(n)回.m4a」パターンのファイルは全て処理できる程度の汎用性が欲しい。

普通ならば、文字列の中から「第」の位置を検索して、そこを起点に切り出し処理を行うわけだが、バッチにはこの種の検索命令がないようだ。なので、「12」の前後の「第」「回」をデリミタとして、文字列を「エントロピーから始める熱力学」「12」「.m4a」の3部分にパターン分解することにする。

@setlocal enabledelayedexpansion
@set st=エントロピーから始める熱力学第12回.m4a
@for /f "usebackq tokens=1-3 delims=第回" %%a in ('!st!') do @(echo %%b)
この例では変数の遅延展開を指定しているが、別にここでは必須ではない。が、現実的にはこの処理はループの中で行われる可能性が高いので、汎用性を考えて遅延展開を有効にしている。

「tokens=1-3」は分割数が3であることを意味する。正確に言えば、1番目から3番目までの要素を変数「%%a」〜「%%c」に入れる指定。ここで欲しい回数「12」は2番目の要素なので「%%b」で参照できる。なお、上記の模擬コードでは「%%a」しか指定していないが、暗黙のうちに「%%b」(2番目の部分)、「%%c」(3番目の部分)も使用される。また、ここではデリミタとして「第」と「回」を使用しているが、デリミタは全角文字でも構わないようだ。

4つ以上の要素があると(デリミタが3箇所以上に出てくると)、4番目以降の要素は切り捨てられる。たとえば「仏教と回教第8回.m4a」だと「回」が2箇所、「第」が1箇所あるので、「仏教と」「教」「8」「.m4a」の4つの部分に分解され、回数「8」は3番目の要素(%%c)になる。4番目の要素「.m4a」は切り捨てられて参照不能になる。

検索対象になる文字列はシングルクオート「'」で括る必要がある('!x!')。この「'」を忘れると正常に機能しないので注意。

本来この構文は、バッククォート「`」で括ったコマンドの実行結果を文字列として順次処理するもの。「usebackq」もここから来ている。 従って、本来ならばin ('!x!')ではなく、in (`echo !x!`)とでもすべきところだが、少なくとも単純に環境変数の中身を参照するだけなら、上記のように「'」で括るだけでも良いようだ。

この手法の問題点は、単一文字しか検索できない点にある。しかし、同様な処理を複数回繰り返すことで、複文字列を検索することも可能になる。たとえば、「山田」という文字列を探すには…

@setlocal enabledelayedexpansion
set st=河田と山川が山田の家に行った
for /f "usebackq tokens=1* delims=山" %%a in ('!st!') do (
  set st=%%b
  if "!st!"=="" (exit /b)
  for /f "usebackq tokens=1* delims=田" %%a in ('!st!') do (
     if "%%a"=="" (echo あった!:山田%%b)
  )
)
みたいに1文字ずつ処理していかないと、正確な処理ができない。物凄く面倒臭い。たとえば、3文字からなる文字列を検索する場合、まずは頭の1文字を検索して、見つかった位置から2文字を切り出して比較する、という手法は取れない(同じ文字が2つ続く場合を想定すると判りやすい)。ちょいとトリッキーな方法としては、同じ対象文字列を「山」と「田」で別々にパターン分割して、分割された部分文字列の長さで連続しているか否かを判断する、というのもありかな…。いずれにしろ、バッチ処理は用途や出現する文字が強く限定されている場合が殆どなので、あまり汎用性を求める必要はないかもしれない。


【DOSとバッチ目次】 【ホーム】