Python:先頭に空白が続く行を前の行につなげて1行にする

Pythonのロゴ Python

本記事のソースコードと実行用の元データファイル(commands.txt)はこちらからダウンロード可能です。

元データと整形後のデータイメージ

YAMAHAルータのコマンド一覧をターミナルから表示したときの結果をテキストに保存したら次のようなデータとなった。これを元データとする。

元データ

administrator:          管理ユーザとしてログインします
administrator password: 管理パスワードを設定します
administrator password encrypted: 管理パスワードを設定します
                        設定されたパスワードは暗号化して保存されます
administrator radius auth: 管理ユーザへの移行時のパスワード認証にRADIUSを使用
                        するか否かを設定します
alarm batch:            バッチファイル実行機能に関連するアラームを鳴らすかどう
                        かを設定します
alarm entire:           有効になっているアラーム音を鳴らすか、全く鳴らさないか
                        を設定します
alarm http revision-up: HTTPリビジョンアップ機能に関連するアラームを鳴らすかど
                        うかを設定します
alarm lua:              Luaスクリプト機能に関連するアラームを鳴らすかどうかを
                        設定します
alarm mobile:           携帯端末接続時にアラームを鳴らすかどうかを設定します
alarm sd:               microSD機能に関連するアラームを鳴らすかどうかを設定し
                        ます

コマンドとその説明文というテキストデータだが、行によって

コマンド名: 説明文

という感じで1行で収まっている行と

コマンド名: 説明文
      説明文つづき
      説明文つづき

などと複数行に説明文が分かれてしまっている行がある。(何行に分かれているかはコマンド名ごとにまちまちである)

説明文が続く場合の特徴として、先頭から空白文字が複数回続いている場合であった。

よって正規表現の「先頭から空白文字が複数回づづく」というパターンを使って最終的なテキストを以下のように出力するようにした。

整形後のデータ

もともと無かった行番号を先頭につけています。

1	administrator:          管理ユーザとしてログインします
2	administrator password: 管理パスワードを設定します
3	administrator password encrypted: 管理パスワードを設定します設定されたパスワードは暗号化して保存されます
4	administrator radius auth: 管理ユーザへの移行時のパスワード認証にRADIUSを使用するか否かを設定します
5	alarm batch:            バッチファイル実行機能に関連するアラームを鳴らすかどうかを設定します
6	alarm entire:           有効になっているアラーム音を鳴らすか、全く鳴らさないかを設定します
7	alarm http revision-up: HTTPリビジョンアップ機能に関連するアラームを鳴らすかどうかを設定します
8	alarm lua:              Luaスクリプト機能に関連するアラームを鳴らすかどうかを設定します
9	alarm mobile:           携帯端末接続時にアラームを鳴らすかどうかを設定します
10	alarm sd:               microSD機能に関連するアラームを鳴らすかどうかを設定します

複数行に続く説明文はまとめて1行とするようにした。
要するに先頭に空白が続く行の空白をとって、説明文の先頭行とつなげて1行にまとめた。

イメージ

Pythonソースコード

to_1line.py

# -*- charset: utf-8 -*-
"""
	先頭に空白が続く行を前の行につなげて1行にする
"""
import re

# ファイルからデータ取得
f = open("commands.txt", encoding="utf-8")
lines = f.readlines()	# 1行ずつリストに読み込む
f.close()

# 最終的な表示用リスト
command_lists = []

# 「先頭に空白が続く」というパターンを生成
pat = re.compile("^[ ]+")

# 行を整形
for line in (lines):
	# 「先頭に空白が続く」というパターンにマッチした行をチェックする
	head = pat.match(line)

	if head is None:	# マッチしない
		command_lists.append(line)	# 表示用リストに行を追加
		prev_line = line			# 現在の行データを保存しておく
	else:				# マッチした
		work = prev_line.replace("\n", "")	# 1つ前の行の改行文字を消去
		line = line.replace(head.group(), "")	# 先頭から続く空白を全て消去(head.group()はマッチした文字列そのもの)
		command_lists.remove(prev_line)		# 表示用のリストから1つ前の行を取り除く
		prev_line = work + line				# 改行を取り除いた行と空白を取り除いた行を繋げる
		command_lists.append(prev_line)		# 表示用リストに追加

# ファイルを書き込みモードで開く
f = open("command_list.txt", mode="w", encoding="utf-8")

# ファイルに表示用リストを書き込む
i = 0
for command in command_lists:
	i+=1
	print("{}\t{}".format(i, command), end="")	# 書き込み内容を画面にも表示
	f.write("{}\t{}".format(i, command))		# ファイルに書き込む

f.close()

前項で出てきた元データと整形後のデータのプログラム上でのファイル名は以下の通り。

元データ: commands.txt(入力ファイル)
整形後のデータ: command_list.txt(出力ファイル)

先頭に空白が続く」というパターンを正規表現で生成している部分

pat = re.compile("^[ ]+")

このパターン(pat)に文字列(line)の先頭がマッチする行をチェックするためには、matchメソッドを使う。(lineは読み込んだ行の文字列変数名)

head = pat.match(line)

上記のコードで変数headには、マッチした場合マッチオブジェクトが返されます。マッチしない場合は、Noneが返されます。

参考

re --- 正規表現操作
ソースコード: Lib/re/ このモジュールは Perl に見られる正規表現マッチング操作と同様のものを提供します。 パターンおよび検索される文字列には、Unicode 文字列 ( str) や 8 ビット文字列 ( bytes) を使い...

このmatchメソッドの戻り値を使ってif文で1行にするか複数行をまとめるかの処理をしています。

	if head is None:	# マッチしない
		command_lists.append(line)	# 表示用リストに行を追加
		prev_line = line			# 現在の行データを保存しておく
	else:				# マッチした
		work = prev_line.replace("\n", "")	# 1つ前の行の改行文字を消去
		line = line.replace(head.group(), "")	# 先頭から続く空白を全て消去(head.group()はマッチした文字列そのもの)
		command_lists.remove(prev_line)		# 表示用のリストから1つ前の行を取り除く
		prev_line = work + line				# 改行を取り除いた行と空白を取り除いた行を繋げる
		command_lists.append(prev_line)		# 表示用リストに追加

正規表現は普段使っていないとすぐに忘れて調べることになりますが、今回のデータ行は1800行以上あったので手作業でするよりはいちいち調べたとしてもプログラムを作ってしまった方が早かったです。(といってもわたしのプログラミング速度は遅い訳ですが)

この記事のプログラムを使って作ったデータの紹介記事が以下です。

コメント

タイトルとURLをコピーしました