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

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

ただいまの
回答率

90.83%

  • Python 3.x

    4880questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

クリップボードにある文字列のうち、{}で囲まれたものを、辞書から検索し、置換したい

解決済

回答 2

投稿

  • 評価
  • クリップ 2
  • VIEW 214

H.Kazuyuki

score 12

【やりたいこと】
pythonを用いて、クリップボードに入った文字列のうち、{}で囲まれたものを、
置換用の辞書にあるキーと一致した場合に、値の文字列に一斉に置換するプログラムを作りたいと思っています。

(例)
辞書ファイル(rep_dict.txt)
郵便番号,〒100-8111
住所,東京都千代田区千代田1-1
宛先指名,山田 太郎
案件,H社向け××システム

クリップボードの文字:
{宛先指名}様
先月ご依頼いただきました{案件}についてですが、…(中略)

以下住所に書類を送付いただけませんでしょうか。
{郵便番号}  {住所} 宛

お忙しい中大変申し訳ありませんが、ご対応のほど、よろしくお願いいたします。

【やってみたこと】
下記コードを描いてみました。

import pyperclip, re, threading, random

def task():
    copy_text = str(pyperclip.paste())

    #todo:ここで読み込む。下はテストで置き替え。{}で囲まれた文字は****で置き換えはできた
#    new = re.sub(r'{.+}', '****', copy_text)

    if len(copy_text) > 0:
        pyperclip.copy(''.join(new))
#        print('---Rewrite Complete!---')
    else:
        print('---Failure---')
    th = threading.Timer(5, task)
    th.start()

t = threading.Thread(target=task)
t.start()


上記のコードで、定期的に{}内の文字を****に置換するのには成功したのですが、
辞書を読み込んで、その情報と一致したら置換する処理を描こうとして詰まってしまいました。

下記のコードを追加したら、辞書を読み込む際に、エラーが出てしまいました。

with open("rep_dict.txt") as f:
s = f.read()
print(s)
dict_rep = dict(line.strip().split(',') for line in s)
dict_rep

ValueError: dictionary update sequence element #0 has length 1; 2 is required

もしよろしければ、よい解決策についてご教示いただければと考えます。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+5

エラーの原因

下記のコードを追加したら、辞書を読み込む際に、エラーが出てしまいました。

with open("rep_dict.txt") as f:
    s = f.read()
    print(s)
    dict_rep = dict(line.strip().split(',') for line in s)
    dict_rep

ValueError: dictionary update sequence element #0 has length 1; 2 is required

期待に反して、lineに各行が与えられていないからです。

print([line for line in s])   # => ['郵', '便', '番', '号', ',', '〒', '1', '0', '0', '-', '8', '1', '1', '1', '\n', '住', '所', ',', '東', '京', '都', '千', '代', '田', '区', '千', '代', '田', '1', '-', '1', '\n', '宛', '先', '指', '名', ',', '山', '田', '\u3000', '太', '郎', '\n', '案', '件', ',', 'H', '社', '向', 'け', '×', '×', 'シ', 'ス', 'テ', 'ム']

次のように書けば良いです。

dict_rep = dict(line.strip().split(',') for line in s.split('\n'))

また、さらに簡潔にも書けます。

with open("rep_dict.txt") as f:
    # s = f.read()
    # print(s)
    dict_rep = dict(line.strip().split(',') for line in f)
    print(dict_rep)

書いてみた

面白そうなので、ちょっと書いてみました。

from collections import defaultdict
import re

#
# 本当はjsonで扱いたいところ
rep_dict = defaultdict(str)
with open(r'rep_dict.txt') as f:
    for row in f:
        row = row.strip()
        key, value = row.split(',')
        rep_dict[key] = value

#
#
def my_replace(match):
    key = match.group().strip('{}')
    return rep_dict[key]

src = open(r'clipboard.txt').read()
dst = re.sub(r'{.+?}', my_replace, src)

print(dst)

実行結果 Wandbox

山田 太郎様
先月ご依頼いただきましたH社向け××システムについてですが、…(中略)

以下住所に書類を送付いただけませんでしょうか。
〒100-8111  東京都千代田区千代田1-1 宛

お忙しい中大変申し訳ありませんが、ご対応のほど、よろしくお願いいたします。

何かご不明な点があれば、お気軽にお尋ねください。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/10 23:23

    迅速なご回答ありがとうございます!
    なるほど、、[line for line in s]だと1文字ずつになってしまうのですね…。
    [line for line in s.split('\n'))]のように改行コードをつかって分割するときちんと取得できるのはなぜなのでしょうか…。基本的な質問で申し訳ありません。。。

    キャンセル

  • 2018/06/10 23:26

    前者は、文字列そのものに対してループしようとしています。
    後者は、文字列を改行文字で分割したリストに対してループしようとしているのです。

    キャンセル

  • 2018/06/10 23:33

    ああなるほど…。文字列扱いで、1文字ずつループになるけど、
    リストに対してのループなら、その分割後の要素に対してのループになるのですね、、ありがとうございます!

    キャンセル

+4

おもしろいプログラムですね。変換テーブルのファイルを読み込むのは LouiS0616 さんが解説なさっている通りです。CSVであれば csv モジュールを使うのがおすすめですが。

今回のように { ほげほげ } のような決められた記法で書かれたテキストに別の文字列を埋め込む仕組みを「テンプレート」と呼びます。Python では Jinja2 という大変有名なテンプレートエンジンがあります。自前で {…} を検索して文字を入れていってもよいですが、テンプレートエンジンを使うことによってより簡単に、より正確に、そしてより柔軟な事ができるようになります。

以下サンプルで書いてみました。

import csv
import time

from jinja2 import Environment
import pyperclip


if __name__ == '__main__':
    with open("rep_dict.txt") as f:
        reader = csv.reader(f)
        dict_rep = {row[0]: row[1] for row in reader}

    clip_last = None
    while True:
        clip = pyperclip.paste()
        if clip != clip_last:
            print('--- Clipboard changed ---')
            new_clip = Environment().from_string(clip).render(dict_rep)
            if clip != new_clip:
                pyperclip.copy(new_clip)
                print('--- Rewrite Complete! ---')
        clip_last = new_clip
        time.sleep(1)
{{ 宛先指名 }}様
先月ご依頼いただきました{案件}についてですが、…(中略)

以下住所に書類を送付いただけませんでしょうか。
{{ 郵便番号 }}  {{ 住所 }} 宛

お忙しい中大変申し訳ありませんが、ご対応のほど、よろしくお願いいたします。

↑ Jinja2 では変数の埋め込みに {{ 変数名 }} という記法を使います。


次に応用編です。テンプレートエンジンを使うとより柔軟にテキストを生成することができます。

{
    "郵便番号": "〒100-8111",
    "住所": "東京都千代田区千代田1-1",
    "宛先指名": "山田 太郎",
    "案件": "H社向け××システム",
    "必要書類": [
        "見積もり確認書",
        "要求案件シート",
        "仮契約書"
    ]
}
import json
import time

from jinja2 import Environment
import pyperclip


if __name__ == '__main__':
    with open('rep_dict.json', 'r', encoding="utf-8") as f:
        dict_rep = json.load(f)

    clip_last = None
    while True:
        clip = str(pyperclip.paste())
        if clip != clip_last:
            print('--- Clipboard changed ---')
            new_clip = Environment().from_string(clip).render(dict_rep)
            if clip != new_clip:
                pyperclip.copy(new_clip)
                print('--- Rewrite Complete! ---')
            clip_last = new_clip
        time.sleep(1)
{{ 宛先指名 }}様
先月ご依頼いただきました{案件}についてですが、…(中略)

書類を送付いただけませんでしょうか。

送付先:
{{ 郵便番号 }}  {{ 住所 }} 宛

送付書類:
{% for 書類名 in 必要書類 %}・{{ 書類名 }}
{% endfor %}
以上{{ 必要書類|length }}点

お忙しい中大変申し訳ありませんが、ご対応のほど、よろしくお願いいたします。
山田 太郎様
先月ご依頼いただきました{案件}についてですが、…(中略)

書類を送付いただけませんでしょうか。

送付先:
〒100-8111  東京都千代田区千代田1-1 宛

送付書類:
・見積もり確認書
・要求案件シート
・仮契約書

以上3点

お忙しい中大変申し訳ありませんが、ご対応のほど、よろしくお願いいたします。

↑ このようにデータが複数ある場合でも対応できますし、要素の数を計算したりと様々な処理が可能です

Jinja2 はWebアプリケーションでHTMLを生成したり、メールの定型文を作ったりなど、動的にテキストを生成する用途に広く使えます。覚えて損はありませんのでぜひ触ってみて下さい。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/10 23:24

    ありがとうございます!
    なるほど、jinja2という便利なライブラリがあるんですね…。
    テンプレートエンジン、ですか…。非常に便利そうなので、ちょっとググって使えるようにしてみます!

    キャンセル

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

  • ただいまの回答率 90.83%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 受付中

    特定位置にある文字列の出現頻度 python

    以下のようなテキストファイルがあります. pythonを用い,'['の直後のIDのみ,出現回数をカウントしたいと考えています. しかし,3つ目のクォーテーション内にも同じIDが出現

  • 受付中

    python UnicodeEncodeError無視する

    こちらの質問https://teratail.com/questions/23204をしたものです. 追加で少し, 最悪,エラーを起こしている文字は取得できなくていいので

  • 解決済

    pythonのエラー

    testThreadが見つからないというメッセージが出てきたのですが、どこに問題があるのでしょうか? よろしくお願いします コード #-*- coding utf-8-*-

  • 解決済

    iOSでWeb APIを叩いてJSONをパース...できません

    前提・実現したいこと iOSでAPIを利用したい。  前にも似たような質問をしているのでお恥ずかしい限りですが、いまいち通信をする部分の作り方がよく分かっていません。  事情

  • 受付中

    Watson の speech to text を HTTP REST で呼び出す方法

    前提・実現したいこと 【IBM Watson音声認識のAPI呼び出し】 IBM Watson の HTTP REST API で speech to text の recogn

  • 解決済

    行列データ 配列 格納

    前提・実現したいこと 3行3列のテキストデータを読み込み3行3列の配列に格納 行数は不確定にする予定 お願いいたします。 発生している問題・エラーメッセージ 1行

  • 解決済

    Pythonのthreadingにおける、終了時の処理

    前提・実現したいこと Pythonを使って、動画や画像を表示するGUIを作っています。その中で、みなさまのご支援を頂ながらカメラの画像を取り込んで再生できるところまで来ました。

  • 解決済

    既存のファイルを複数の指定の行数で分割保存する方法はありますか?

    前提・実現したいこと 膨大なデータ(excelで開けません)を指定の行で分割保存したいです。 その時、例えば、合計10000行のデータを、 1~1000行で1ファイル(1000行)

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

  • Python 3.x

    4880questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。