質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.35%

  • Python

    3821questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • 正規表現

    568questions

    正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

  • 置換

    34questions

    置換とは文字列中の特定の文字に対して、別の文字列に置き換えることを指します。

Python re.sub()の正規表現

受付中

回答 4

投稿 2017/12/05 16:34 ・編集 2017/12/06 13:48

  • 評価
  • クリップ 0
  • VIEW 109

roy29

score 11

Python3でre.sub()を使った正規表現による置換に関して質問です。

例えばa = 'aaa, bbb[12, 13, 14]. ccc.'といった文字列の角かっこに囲まれた数字を'[12] [13] [14]'のようにそれぞれを角かっこで囲うように変換したいです。


追記
すみません。例が不適切でした。
今回の質問は論文の引用表記を置換したいというものでした。
http://journals.plos.org/plosmedicine/article?id=10.1371/journal.pmed.0030232


問題は単純にカンマを角かっこに置換するだけではないという点です。
文章中に含まれている他のカンマは置き換えたくないです。

つまり、第一引数は以下のようになるかと思います。
re.sub('\[(\d+, )+(\d+)\]', '', a)

この条件で合致したときだけ', 'を'] ['に変換したいのですが、後方参照をどのように利用すればいいのかわかりません。
re.sub('\[(\d+, )+(\d+)\]', r'[\1] [\2]', a)としたところ'[13, ] [15]'となってしまいました。

アドバイスをいただけたら幸いです。

*追記
正規表現を使わない方法については以下のような方法で解決できました。
引数はa.strip()です。

def get_refer(words):
    start = 0
    dic = []
    for tmp in words:
        if re.match('\[\d+\]', tmp):
            dic.append(tmp)
            continue
        if re.match('\[\d+(-|–)\d+\]', tmp):
            dic.append(tmp)
            continue
        if re.match('\[\d', tmp):
            start = 1
            num = tmp.strip('[').strip(',')
            fin = '['+num+']'
            # print(fin)
            dic.append(fin)
            continue
        # if re.match('(\d+-)?\d+\]', tmp):
        if re.match('.*\d+\]', tmp):
            start = 0
            num = tmp.strip(']')
            fin = '['+num+']'
            # print(fin)
            dic.append(fin)
            continue
        if start == 1:
            tmp = tmp.strip(',')
            fin = '['+tmp+']'
            # print(fin)
            dic.append(fin)
        else:
            dic.append(tmp)
    return dic
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • LouiS0616

    2017/12/05 16:38

    正規表現を用いなくとも目的は簡単に達成可能に思います。変換が出来れば手段は問わないのか、あくまで正規表現を学習したいのか、どちらでしょうか?

    キャンセル

  • roy29

    2017/12/05 16:50

    コメントありがとうございます。情報を追加しました。追記のような条件分岐をつけて解決はできるのですが、もっとスマートに書く方法があれなお聞きしたいです。

    キャンセル

回答 4

0

正規表現を使わなくてもよいのなら、こんな感じでしょうか。

import json

a = '[12, 13, 14]'
dst_str = ''
for elem in json.loads(a):
    dst_str += '[' + str(elem) + '] '

print(dst_str)

読みづらいですが、こういうのもありですね。
上記に比べ、最後に無駄な空白がはいらない点が優れています。

import json

a = '[12, 13, 14]'
dst_str =  ' '.join('[' + str(e) + ']' for e in json.loads(a))
print(dst_str)

良く考えたらリストに変換する必要もないか。

a = '[12, 13, 14]'
a = a.replace(', ', '] [')
print(a)

投稿 2017/12/05 16:55

編集 2017/12/05 17:03

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/05 17:04

    ご回答ありがとうございます。
    すみません、説明があまりよろしくありませんでした。

    文を編集しましたが、実際には`a = 'aaa, bbb[12, 13, 14]. ccc.'`のような文字列が対象となります。

    キャンセル

  • 2017/12/05 17:05

    [ ] で囲まれている部分を正規表現で切り出して、回答のように置き換えればいいのでは。

    キャンセル

0

ちょっと対症療法的すぎるかも知れませんけど、別発想で考えてみました。 

a  = "[12, 34, 56]"

# 数字に続くコンマを置換 
a1 = re.sub('(?<=\d),', ']', a)

# スペースの後に[が存在しないなら補う 
a2 = re.sub(' (?!\[)' , '[', a1)

追記 
質問文の補足説明を見て、もう一度考えてみました。 
下記#1では、"数字または角開き括弧に続かない数字"の前に角開き括弧を置きます。
下記#2では、"数字に続くカンマ"を角閉じ括弧に置換します。

import re
for a in ["[12, 34, 56]", "[12,34, 56]", "[7]", "a = 'aaa, bbb[12, 13, 14]. ccc.'"]:
  a1=re.sub('(?<![[\d])(?=\d+)', '[', a)  #1 
  a2=re.sub('(?<=\d),'         , ']', a1) #2
  print(a)
  print(a1)
  print(a2)
  print('')

投稿 2017/12/05 17:05

編集 2017/12/06 17:59

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/06 11:05

    ありがとうございます!
    こちらだと、例えば"5,000"などの通常の数字も置換されてしまいますね。
    すみません、前提条件を網羅的に指定することができていませんでした。

    キャンセル

  • 2017/12/06 12:45

    では、[1,200, 1300, 1400] のような文字列はどう解釈するのが正しいのでしょうか? その辺ははっきり説明していただかないと正確な回答は無理だと思います。

    キャンセル

0

フォーマットを仮定して正規表現でごり押してみます。

先に数字を抜き出して、文字列に挿入しています。

import re
a = 'aaa, bbb[12, 13, 14]. ccc.'
nums = re.findall('[\d][\d]*', a)
h, e = re.split('\[[\d\D]*\]', a)
s = '{0}{1}{2}'.format(h, ' '.join(map('[{0}]'.format, nums)), e)

投稿 2017/12/05 17:52

編集 2017/12/06 02:29

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

ちょっと冗長ですが僕ならこんな感じですかね。
正規表現で[12, 13, 14]を抜き出してreplaceかけるリストを作ります。
その後に、元の文献にreplaceをかけます。(多分正規表現でヒットする物しかかからないはず。)

import re
def f(match):
    return (match.group(), match.group().replace(', ', '] ['))
l = [f(i) for i in re.finditer('\[\d+, [\d, ]+\]', a)]
for i in l:
    a = a.replace(*i)

ちなみに、[10-14]みたいな記法もあるので、そちらも対応するなら、もう一回同様の操作を行います。

def f2(match):
    return (match.group(),
            '[' + '] ['.join(str(k) for k in range(*(int(j) + i for i,
                                                     j in enumerate(re.findall('\d+', match.group()))))) + ']'
            )
l2 = [f2(i) for i in re.finditer('\[\d+–\d+\]', a)]
for i in l2:
    a = a.replace(*i)

f2がわかりづらいので、内包表記を使わないならこんな感じです。

def f2(match):
    org = match.group()
    # matchオブジェクトに最小,最大の順に二つの文献番号が入っていると決め打ち
    pos = list()  # 最小と最大の二つの数値を入れるリストを用意
    for i, j in enumerate(re.findall('\d+', match.group())):  # 数値を抽出
        pos.append(int(j) + i)  # 最大は1足しておく
    ref_seq = list()
    for k in range(*pos):  # 最小から最大の文献番号まで回す
        ref_seq.append(str(k))
    sub = '[' + '] ['.join(ref_seq) + ']'  # かっこ区切りに文字列生成
    return (org, sub)

投稿 2017/12/08 10:09

編集 2017/12/08 10:16

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.35%

関連した質問

同じタグがついた質問を見る

  • Python

    3821questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • 正規表現

    568questions

    正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

  • 置換

    34questions

    置換とは文字列中の特定の文字に対して、別の文字列に置き換えることを指します。