Pythonで、文字列内のすべての ( ) の前後に空白が入っていなければ、前後に空白を挿入する文字列処理プログラムを考えてみました。但し、 ( が行頭にあったり、) が行末にあったり、カンマ、ピリオド等の句読点が連続する場合は、空白を入れません。一旦、自分の備忘録として記します。
とりあえず、サンプルとして処理する文字列は、'色相(Hue)-彩度(Chroma) ベクトルスコープ(Vectorscope)' です。これの()の前後に空白が入っていない場合は挿入します。この文字列は、tmpEachline というリスト変数に入れておきます。
--------------------------
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import io
import re
# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='UTF-8')
tmpEachline = '色相(Hue)-彩度(Chroma) ベクトルスコープ(Vectorscope)'
print('変更前:', tmpEachline)
# ()の前後に空白がない場合空白を挿入
# '(' の検索 ※要エスケープシーケンス \
t_start = [m.start() for m in re.finditer('\(',tmpEachline)]
# すべての該当要素を列記する finditer メソッドを、内包表記を使って使用 > 要素を 一旦 m に入れ、そこから各始点をt_start配列に格納
print ('発見したindex値', t_start)
n = 0 # n: 空白を挿入したときの、列のずれを記録する変数
for j in t_start:
if j > 0 and (tmpEachline[j-1+n] != ' ' and tmpEachline[j-1+n] != 'n'): #行頭に ( がくる場合は除く
tmpEachline2 = tmpEachline[0:j+n] + ' ' + tmpEachline[j+n:]
# n は列をずらす数
tmpEachline = tmpEachline2
n = n + 1 # 空白を挿入したときに n を一つ増やし、ずらす
# ')' の検索 ※要エスケープシーケンス \
t_start = [m.start() for m in re.finditer('\)',tmpEachline)]
print ('発見したindex値', t_start)
print('文字列の長さ', len(tmpEachline))
n = 0
for j in t_start:
if j < len(tmpEachline) -2 and (tmpEachline[j + 1+n] != ' ' and tmpEachline[j+n] != ' '): if tmpEachline[j + 1+n] != '.' and tmpEachline[j + 1+n] != ',':
if tmpEachline[j + 1+n] != '\\' and (tmpEachline[j + 1+n] != '。' and tmpEachline[j + 1+n] != '、'):
tmpEachline2 = tmpEachline[0:j + n + 1] + ' ' + tmpEachline[j + n + 1:]
tmpEachline = tmpEachline2
n = n + 1 # 空白を挿入したときに n を一つ増やし、ずらす
print('変更後:', tmpEachline)
-------------------------
解説:
まず、正規表現操作を行うライブラリー re を呼び出しておきます。
一旦、tmpEachlineに入れた文字列を、確認のため
print ('変更前:', tmpEachline)
で、コンソール出力します。
さらに、対象の文字列の中でパターン'('とマッチしたすべてのマッチオブジェクトを取得するためのイテレータを取得する、finditerメソッドと内包表現を使い、このイテレータを m に取得、さらに、mに対して、start メソッドを使い、マッチした文字列の始点のindexを取得します。
t_start = [m.start() for m in re.finditer('\(',tmpEachline)]
具体的には、文字パターン ( の始点を t_start変数に取得することになります。この取得した始点をprint文で書かせていますが、 この文字列の場合、[2, 10, 27] になります。
その後 t_start に格納した各始点ごとに、for文を使って以下の内容を繰り返し処理します。
if j > 0 and (tmpEachline[j-1+n] != ' ' and tmpEachline[j-1+n] != 'n'):
この文は、( が行頭に来る場合及び、( の直前に空白があるか、直前にnがある場合を除いて(改行\n が直前にあることを想定)、それ以下の文を実行する、という文です。
この時、( の位置を検索するtmpEachline[j-1+n] の検索インデックスに +n を加えています。これはなぜかというと、n は、その後の処理で空白を挿入した回数をカウントする変数なのです。t_start を取得する時点では空白を挿入していませんが、その後の処理で空白を挿入していきます。従って、空白を一つ挿入するごとに、1文字ずれますので、そのずれを調整するのが、n なのです。nの初期値は0なので、最初は単に始点インデックスの直前の文字が空白かどうかを検査しています。1文字空白を挿入するごとに、検査する文字列も1文字ずつずらしていきます。
tmpEachline2 = tmpEachline[:j+n] + ' ' + tmpEachline[j+n:]
この文で、( の前に空白を挿入します。indexには、やはりズレを調整する n をかませます。因みに、( の直前に空白を挿入するからと言って、tmpEachline[:j-1+n] + ' ' + tmpEachline[j+n:] とやってしまうと、挿入ではなく ( を空白で上書きしてしまいます。これは、配列の index が0から始から言語では、 始点番号 ≦ 取得範囲 < 終点番号となるためです。したがって、string[:j]とやると、string[0]から始まって、jの直前の文字列(string[j-1])まで取得します。string[j:]だとstring[j]から始まって最後まで取得します。
終わったらtmpEachline2をtmpEachlineに代入し、さらに空白の挿入回数をカウントするカウンター変数 n を1つ進めます。
以上すべての ( について処理が終わりましたら、次は ) について同様に処理します。ただ ) が文末または改行末直前にあったり、次に , や . 、。が続く場合は空白を挿入しません。
実行結果は下記の通りです。
-----------------
変更前: 色相(Hue)-彩度(Chroma) ベクトルスコープ(Vectorscope)
発見したindex値 [2, 10, 27]
発見したindex値 [7, 19, 42]
文字列の長さ 43
変更後: 色相 (Hue) -彩度 (Chroma) ベクトルスコープ (Vectorscope)
---------------
しっかり、( ) の前後に空白が挿入されていることが確認できました。
「発見したindex値」は上の行は ( の位置を、下の行は、 ) の位置を出力しています。
( は、行の中の 3, 8, 28文字目にありますが、文字列の index は 0 から始まるので、それより1引いた値が出力されています。