悩み
こういうやり辛そうな文字列から,不要なコメントのみを削除したい.
が,この場合,ハッシュ記号 (#) の全てを消すとまずい (引用符内にあるハッシュを削除しないようにしなくてはならない…).
cmd = """
# これは消さなきゃダメ
x <- 3 # これは消さなきゃダメ
paste('# これは消しちゃダメ')
paste('''
# これは消しちゃダメ
''')
paste('"# これは" 消しちゃダメ')
"""
方法
ハッシュの位置 (hash_ids
) を取得した後,さらに絞り込みをかけ,コメントを示すハッシュの位置 (comment_ids
) を取得する.
from typing import List
from copy import copy
import numpy as np
def count_quote_marks(x: str) -> List[int]:
"""各種引用符の数を数える"""
n_tquotes = x.count('\'\'\'')
n_tdquotes = x.count('\"\"\"')
n_quotes = x.count('\'') - 3 * n_tquotes
n_dquotes = x.count('\"') - 3 * n_tdquotes
return [n_quotes, n_dquotes, n_tquotes, n_tdquotes]
def get_comment_ids(cmd: str) -> List[int]:
"""「コメントを示す」ハッシュのインデックスを取得"""
# quotes, double quotes, triple quotes, triple double quotesの数を保持するリスト
quotes_counts = np.zeros(4, dtype=int)
# ハッシュ (#) の位置を返すジェネレーター
hash_ids = (i for i, elem in enumerate(cmd) if '#' in elem)
# hash_idsのうち,引用符内に無いハッシュのインデックスをcomment_idsとする
prev_i = 0
comment_ids = []
for i in hash_ids:
sliced = cmd[prev_i: i]
quotes_counts += np.array(count_quote_marks(sliced))
# 全quotes_countsが偶数 <=> 引用符外にあるハッシュ
if np.sum((quotes_counts) % 2) == 0:
comment_ids.append(i)
prev_i = i
return comment_ids
def get_return_ids(cmd: str) -> List[int]:
"""改行記号\nを取得"""
return [i for i in range(len(cmd)) if cmd[i: i + 1] == '\n']
def delete_useless_comments(cmd: str) -> str:
"""不要なコメントを削除する"""
cmd = copy(cmd)
comment_ids = get_comment_ids(cmd)
return_ids = get_return_ids(cmd + '\n')
next_return_ids = [] # ハッシュ直後の改行コードを取得
for index in comment_ids:
next_return_ids.append([i for i in return_ids if i > index][0])
# 後ろからcmdを削っていかないとインデックス指定が面倒なので
for i, j in zip(reversed(comment_ids), reversed(next_return_ids)):
cmd = cmd[:i] + cmd[j:]
return cmd
結果
いけた.
print(cmd, end='\n' + '=' * 50 + '\n')
print(delete_useless_comments(cmd))
# これは消さなきゃダメ
x <- 3 # これは消さなきゃダメ
paste('# これは消しちゃダメ')
paste('''
# これは消しちゃダメ
''')
paste('"# これは" 消しちゃダメ')
==================================================
x <- 3
paste('# これは消しちゃダメ')
paste('''
# これは消しちゃダメ
''')
paste('"# これは" 消しちゃダメ')
余談
「pypeRに引き渡すコマンドにコメント#が含まれているとpypeRがフリーズする」という悩みがきっかけだったので,cmdがRのスクリプトになっている…