入力ファイルの形式が面倒です。入力を空白で分割することもできますが、キャプチャするフィールドの一部には空白が含まれている必要があります。入力を列番号で分割することもできますが、すべての文字列が常に同じ長さであるかどうかはわかりません。数字の桁数が異なる可能性が高いようです。したがって、最善の解決策には正規表現を使用する必要があります。
この行全体を解析するための単一の正規表現は、記述して理解するのが非常に面倒です。しかし、短いパターンからパターンを構築することはできます。結果は分かりやすいと思います。また、ファイル形式が変わったり、キャプチャしたいフィールドが変わったりした場合は、これを簡単に変更できると思います。
*
短いパターンを繰り返すために、Python の「文字列繰り返し」演算子 を使用していることに注意してください。認識してキャプチャしたい単語が 2 つある場合はc*2
、キャプチャ パターンを 2 回繰り返すことができます。
目的の出力の例では、余分な空白がありました。空白が入らないようにパターンを書きましたが、実際に空白が必要な場合は、パターンを自由に編集できます。
正規表現についてよくわからない場合は、Pythonre
モジュールのドキュメントを読む必要があります。簡単に言えば、括弧で囲まれたパターンの部分がキャプチャされ、他の部分は一致しますがキャプチャされません。 \s
空白に\S
一致し、非空白に一致します。 +
パターン内の は「1 以上」を*
意味し、「0 以上」を意味します。 ^
パターンの先頭と$
末尾にそれぞれ一致します。
import re
# Define patterns we want to recognize.
c = r'(\S+)\s+' # a word we want to capture
s = r'\S+\s+' # a word we want to skip
mesg = r'(\S.*\S)\s+--Sev\s+' # mesg to capture; terminated by string '--Sev'
w2 = r'(\S+\s+\S+)\s+' # two words separated by some white space
w2semi = r'(\S+\s+\S+)\s*;\s+' # two words terminated by a semicolon
tail = r'(.*\S)\s*;'
# Join together the above patterns to make one giant pattern that parses
# the input.
s_pat = ( r'^\s*' +
c*2 + s*3 + c*1 + s*10 + c*2 + s*14 + c*1 + s*14 +
mesg + w2 + w2semi*2 + tail +
r'\s*$')
# Pre-compile the pattern for speed.
pat = re.compile(s_pat)
# Test string and the expected output result.
s_input = "83b14af0-949b-71e0-18d5-0ad781020000 40ba8352-8dd2-71dc-12b8-0ad781020000 1 -1407714483 20 COLG-GRA-617-RD1.oss 1 181895426 12 oss-ap-1.oss 0 0 48 0 0 0 1307845644 1307845647 0 2 12 0 0 0 0 0 12 0 0 0 0 0 1307845918 3 OpC 6 opcecm 9 SNMPTraps 8 IBB_COLG 4 ATM0 0 0 0 69 Cisco Agent Interface Up (linkUp Trap) on interface ATM0 --Sev Normal 372 Generic: 3; Specific: 0; Enterprise: .1.3.6.1.4.1.9.1.569;"
s_correct = "83b14af0-949b-71e0-18d5-0ad781020000|40ba8352-8dd2-71dc-12b8-0ad781020000|COLG-GRA-617-RD1.oss|1307845644|1307845647|1307845918|Cisco Agent Interface Up (linkUp Trap) on interface ATM0|Normal 372|Generic: 3|Specific: 0|Enterprise: .1.3.6.1.4.1.9.1.569"
# re.match() returns a "match group"
m = re.match(pat, s_input)
# m.groups() returns sequence of captured strings; join with '|'
s_output = '|'.join(m.groups())
# sanity check
if s_correct == s_output:
print "excellent"
else:
print "bogus"
# excellent.
パターンを作成し、テストし、デバッグしたので、実際にファイルを処理するプログラムを作成するのは非常に簡単です。
# use the pattern defined above, named "pat"
with open(input_file, "r") as f_in, open(output_file, "w") as f_out:
for line_num, line in enumerate(f_in, 1):
try:
m = re.match(pat, line)
s_output = '|'.join(m.groups())
f_out.write(s_output + '\n')
except Exception:
print("unable to parse line %d: %s" % (line_num, line)
これにより、一度に 1 行ずつファイルが読み取られ、その行が処理され、処理された行が出力ファイルに書き込まれます。
with
1 行で複数のステートメントを使用していることに注意してください。これは最近の Python では動作しますが、2.5 または 3.0 では動作しません。