【正規表現】re.subを使った高度な文字列置換

# 目次
- [結論](#結論)
- [re.subとは](#resubとは)
- [どういう時に使う](#どういう時に使う)
- [置換対象 repl に文字列を渡す](#置換対象-repl-に関数を渡す)
  - [解説](#解説) 
- [高度な置換とは](#高度な置換とは)
  - [置換対象 repl に関数を渡す](#置換対象-repl-に関数を渡す)   
  - [置換対象 repl に引数が2つ以上の関数を渡す](#置換対象-repl-に引数が2つ以上の関数を渡す)
  - [高度な文字列置換の例](#高度な文字列置換の例) 
  - [【補足】re.subを使わずに高度な文字列置換を行う方法](#補足resubを使わずに高度な文字列置換を行う方法)

# 結論
- re.subの置換対象 replには関数を渡すことができる
- re.subの置換対象 replには`2つ以上引数を持つ関数を渡す`ことができる
- re.subの置換対象 replに2つ以上引数を持つ関数を渡す時には、`lambda式でラップする`必要がある

# re.subとは

- [6.2. re.sub() — 正規表現操作 — Python 3.8.0 ドキュメント] (https://docs.python.org/ja/3/library/re.html#re.sub)

>`re.sub(pattern, repl, string, count=0, flags=0)`
string 中に出現する最も左の重複しない pattern を置換 repl で置換することで得られる文字列を返します。パターンが見つからない場合、 string がそのまま返されます。 repl は文字列または関数です。 repl が文字列の場合は、その中の全てのバックスラッシュエスケープが処理されます。 \n は 1 つの改行文字に変換され、 \r はキャリッジリターンに変換される、などです。 ASCII 文字のエスケープで未知のものは将来使うために予約されていて、エラーとして扱われます。 それ以外の \& のような未知のエスケープは残されます。

# どういう時に使う
元の文字列を`保持`しつつ、`正規表現パターンに一致する文字列のみ` 置換したいとき
> ちかん
【置換】
《名・ス他》あるものを他のものに置き換えること。

## 置換対象 repl に文字列を渡す
単純な置換しかできないので、あまり使わない。

```python
>>> import re
>>> s = """string 中に出現する最も左の重複しない pattern を置換 repl で置換することで
得られる文字列を返します。 パターンが見つからない場合、 string がそのまま返されます。 
repl は文字列または関数です。 repl が文字列の場合は、その中の全ての
バックスラッシュエスケープが処理されます。 \n は 1 つの改行文字に変換され、 \r はキャリッジ
リターンに変換される、などです。 ASCII 文字のエスケープで未知のものは将来使うために予約され
ていて、エラーとして扱われます。 それ以外の \& のような未知のエスケープは残されます。 \6 の
ような後方参照は、パターンのグループ 6 がマッチした部分文字列で置換されます。"""
>>> s
'string 中に出現する最も左の重複しない pattern を置換 repl で置換することで
得られる文字列を返します。 パターンが見つからない場合、 string がそのまま返されます。 
repl は文字列または関数です。 repl が文字列の場合は、その中の全ての
バックスラッシュエスケープが処理されます。 \n は 1 つの改行文字に変換され、 \r はキャリッジ
リターンに変換される、などです。 ASCII 文字のエスケープで未知のものは将来使うために予約され
ていて、エラーとして扱われます。 それ以外の \\& のような未知のエスケープは残されます。 \x06 の
ような後方参照は、パターンのグループ 6 がマッチした部分文字列で置換されます。'
>>> md_s = re.sub(r'(?P<arg>[pattern|repl|string]+)', r'`\g<arg>`' , s)  # 文字列パターン[]に一致した文字列をMarkdownのコードのインライン表示にする
>>> md_s
'`string` 中に出現する最も左の重複しない `pattern` を置換 `repl` で置換することで
得られる文字列を返します。 パターンが見つからない場合、 `string` がそのまま返されます。 
`repl` は文字列または関数です。 `repl` が文字列の場合は、その中の全ての
バックスラッシュエスケープが処理されます。 \n は 1 つの改行文字に変換され、 \r はキャリッジ
リターンに変換される、などです。 ASCII 文字のエスケープで未知のものは将来使うために予約され
ていて、エラーとして扱われます。 それ以外の \\& のような未知のエスケープは残されます。 \x06 の
ような後方参照は、パターンのグループ 6 がマッチした部分文字列で置換されます。'
```

# 高度な置換とは
`正規表現パターンに一致する文字列`に応じて、置換対象となる文字列を変更したい。
以下のテキストファイル(変数s)で#から始まる変数（例: #LAST_NAME）に応じて、文字列を置換するケースを考える。

```python

txt = """#LAST_NAME #FIRST_NAME 様
この度はxxチケットをご利用いただき、ありがとうございます。
先日お申込みいただきました下記の抽選申込に関しまして、
厳正な抽選の結果、下記内容にてチケットをご用意いたしました。
以下にお支払い方法や、受取方法など大切なご案内を記載しております。
必ず最後までご確認をお願いいたします。

-----

■お名前カナ

#LAST_NAME_KATAKANA #FIRST_NAME_KATAKANA

■電話番号

#PHONE_NUMBER
 
■メールアドレス

#EMAIL

----- 
■受付番号

#RECEIPT_NUMBER
 
■受付日

2019年 3月 23日 18時 07分

---------------
【当選内容】 
■イベント名: 《先行》 欅坂46 3rd YEAR ANNIVERSARY LIVE [東京公演] 
■受付名称: ◇先行抽選[欅坂46FC] 
<第1希望> 
公演名:〔5/9(木)〕 欅坂46 3rd YEAR ANNIVERSARY LIVE 
公演日:2019年5月9日(木) 18:30 
会場:日本武道館 
申込内容 
指定席 ¥7,800 x 2枚 
チケット代金計:¥15,600 
システム利用料:¥432(税込) 
■お支払 
お支払方法: クレジットカード決済(抽選) 
※決済手数料:¥0/1枚ごと 
<クレジットカードでお支払いの方> 
ご指定のクレジットカードで、下記金額の決済を完了しました。 
決済合計金額:¥16,464(税込) 
※ご当選の場合、ご登録のクレジットカードにて決済完了となります。 
(引取手数料を含めた全額の決済となります)"""
```

## 置換対象 repl に関数を渡す

- [6.2. re.sub() — 正規表現操作 — Python 3.8.0 ドキュメント] (https://docs.python.org/ja/3/library/re.html#re.sub)

> repl は文字列または`関数`です。

### 解説

replに関数を渡した時、呼び出す関数の第一引数にマッチオブジェクトが渡される。

- [6.2. re.sub() — 正規表現操作 — Python 3.8.0 ドキュメント] (https://docs.python.org/ja/3/library/re.html#re.sub)

> repl が関数であれば、それは重複しない pattern が出現するたびに呼び出されます。この関数は一つの マッチオブジェクト 引数を取り、置換文字列を返します。

```python

txt = """#LAST_NAME #FIRST_NAME 様
この度はxxチケットをご利用いただき、ありがとうございます。
先日お申込みいただきました下記の抽選申込に関しまして、
厳正な抽選の結果、下記内容にてチケットをご用意いたしました。
以下にお支払い方法や、受取方法など大切なご案内を記載しております。
必ず最後までご確認をお願いいたします。

-----

■お名前カナ

#LAST_NAME_KATAKANA #FIRST_NAME_KATAKANA

■電話番号

#PHONE_NUMBER
 
■メールアドレス

#EMAIL

----- 
■受付番号

#RECEIPT_NUMBER
 
■受付日

2019年 3月 23日 18時 07分

---------------
【当選内容】 
■イベント名: 《先行》 欅坂46 3rd YEAR ANNIVERSARY LIVE [東京公演] 
■受付名称: ◇先行抽選[欅坂46FC] 
<第1希望> 
公演名:〔5/9(木)〕 欅坂46 3rd YEAR ANNIVERSARY LIVE 
公演日:2019年5月9日(木) 18:30 
会場:日本武道館 
申込内容 
指定席 ¥7,800 x 2枚 
チケット代金計:¥15,600 
システム利用料:¥432(税込) 
■お支払 
お支払方法: クレジットカード決済(抽選) 
※決済手数料:¥0/1枚ごと 
<クレジットカードでお支払いの方> 
ご指定のクレジットカードで、下記金額の決済を完了しました。 
決済合計金額:¥16,464(税込) 
※ご当選の場合、ご登録のクレジットカードにて決済完了となります。 
(引取手数料を含めた全額の決済となります)"""
REG_VARIABLE = re.compile(r"#(?P<variable>[A-Z|_]+)")


def replace_variable(match):
    m = match.group("variable")
    wakabayashi = {
        "LAST_NAME": "若林",
        "FIRST_NAME": "正恭",
        "LAST_NAME_KATAKANA": "ワカバヤシ",
        "FIRST_NAME_KATAKANA": "マサヤス",
        "EMAIL": "masayasu.wakabayashi",
    }
    if m in wakabayashi:
        return wakabayashi[m]


inserted_text = REG_VARIABLE.sub(replace_variable, txt)  # 置換対象 repl に関数を渡す
print(inserted_text)
>>> '若林 正恭 様
この度はxxチケットをご利用いただき、ありがとうございます。
先日お申込みいただきました下記の抽選申込に関しまして、
厳正な抽選の結果、下記内容にてチケットをご用意いたしました。
以下にお支払い方法や、受取方法など大切なご案内を記載しております。
必ず最後までご確認をお願いいたします。

-----

■お名前カナ

ワカバヤシ マサヤス

■電話番号



■メールアドレス

masayasu.wakabayashi

----- 
■受付番号



■受付日

2019年 3月 23日 18時 07分

---------------
【当選内容】 
■イベント名: 《先行》 欅坂46 3rd YEAR ANNIVERSARY LIVE [東京公演] 
■受付名称: ◇先行抽選[欅坂46FC] 
<第1希望> 
公演名:〔5/9(木)〕 欅坂46 3rd YEAR ANNIVERSARY LIVE 
公演日:2019年5月9日(木) 18:30 
会場:日本武道館 
申込内容 
指定席 ¥7,800 x 2枚 
チケット代金計:¥15,600 
システム利用料:¥432(税込) 
■お支払 
お支払方法: クレジットカード決済(抽選) 
※決済手数料:¥0/1枚ごと 
<クレジットカードでお支払いの方> 
ご指定のクレジットカードで、下記金額の決済を完了しました。 
決済合計金額:¥16,464(税込) 
※ご当選の場合、ご登録のクレジットカードにて決済完了となります。 
(引取手数料を含めた全額の決済となります)'
```

## 置換対象 repl に引数が2つ以上の関数を渡す

re.subの実装上、渡す関数は引数を一つしか取れず、その引数にはmatchオブジェクトが渡される。
- [6.2. re.sub() — 正規表現操作 — Python 3.8.0 ドキュメント] (https://docs.python.org/ja/3/library/re.html#re.sub)

> repl が関数であれば、それは重複しない pattern が出現するたびに呼び出されます。この関数は一つの マッチオブジェクト 引数を取り、置換文字列を返します。

しかし、以下に示すように、1つしか引数を取らない関数に2つ以上引数を渡す方法がある。
結論を言うと、ラムダ式で関数をラップすることで、引数が2つ以上の関数を渡すことができる。

- [python - Passing two arguments to replace function for Re.sub - Stack Overflow](https://stackoverflow.com/questions/26496119/passing-two-arguments-to-replace-function-for-re-sub/26503399?stw=2#26503399)

> As has been mentioned, when re.sub calls your function, it only passes one argument to it. The docs indicate this is a match object (presumably the line variable?)

> If you want to pass additional arguments, you should wrap your function up in a lambda expression.

以下のように渡すとエラーが出る。

```python
def replace_variable(match: Match, customer: Dict[str, str]):
    """re.subの置換対象 repl で呼び出す関数
    """
inserted_text = REG_VARIABLE.sub(replace_variable(customer), s)
>>> TypeError: replace_variable() missing 1 required positional argument: 'customer'
```

以下のようにlambda式でラップして渡すとエラーが出ない。

```python
inserted_text = REG_VARIABLE.sub(lambda wrapper: replace_variable(wrapper, customer), s)
```

## 高度な文字列置換の例
以下のpythonファイルを実行すると、下記のような文字列が人数分出力される。

```
"""若林 正恭 様
この度はxxチケットをご利用いただき、ありがとうございます。
先日お申込みいただきました下記の抽選申込に関しまして、
厳正な抽選の結果、下記内容にてチケットをご用意いたしました。
以下にお支払い方法や、受取方法など大切なご案内を記載しております。
必ず最後までご確認をお願いいたします。

-----

■お名前カナ

ワカバヤシ マサヤス

■電話番号

297-2150-4740
 
■メールアドレス

masayasu.wakabayashi@example.com

----- 
■受付番号

06475050
 
■受付日

2019年 3月 23日 18時 07分

---------------
【当選内容】 
■イベント名: 《先行》 欅坂46 3rd YEAR ANNIVERSARY LIVE [東京公演] 
■受付名称: ◇先行抽選[欅坂46FC] 
<第1希望> 
公演名:〔5/9(木)〕 欅坂46 3rd YEAR ANNIVERSARY LIVE 
公演日:2019年5月9日(木) 18:30 
会場:日本武道館 
申込内容 
指定席 ¥7,800 x 2枚 
チケット代金計:¥15,600 
システム利用料:¥432(税込) 
■お支払 
お支払方法: クレジットカード決済(抽選) 
※決済手数料:¥0/1枚ごと 
<クレジットカードでお支払いの方> 
ご指定のクレジットカードで、下記金額の決済を完了しました。 
決済合計金額:¥16,464(税込) 
※ご当選の場合、ご登録のクレジットカードにて決済完了となります。 
(引取手数料を含めた全額の決済となります)"""
```

```python
import random
import re
from typing import Dict, Pattern, List, Match

REG_VARIABLE: Pattern = re.compile(r"#(?P<variable>[A-Z|_]+)")
HINATAZAKA = [
    {
        "LAST_NAME": "井口",
        "FIRST_NAME": "眞緒",
        "LAST_NAME_KATAKANA": "イグチ",
        "FIRST_NAME_KATAKANA": "マオ",
        "EMAIL": "mao.iguchi",
    },
    {
        "LAST_NAME": "潮",
        "FIRST_NAME": "紗理菜",
        "LAST_NAME_KATAKANA": "ウシオ",
        "FIRST_NAME_KATAKANA": "マオ",
        "EMAIL": "sarina.ushio",
    },
    {
        "LAST_NAME": "影山",
        "FIRST_NAME": "優佳",
        "LAST_NAME_KATAKANA": "カゲヤマ",
        "FIRST_NAME_KATAKANA": "ユウカ",
        "EMAIL": "yuka.kageyama",
    },
    {
        "LAST_NAME": "加藤",
        "FIRST_NAME": "史帆",
        "LAST_NAME_KATAKANA": "カトウ",
        "FIRST_NAME_KATAKANA": "シホ",
        "EMAIL": "shiho,kato",
    },
    {
        "LAST_NAME": "齊藤",
        "FIRST_NAME": "京子",
        "LAST_NAME_KATAKANA": "サイトウ",
        "FIRST_NAME_KATAKANA": "キョウコ",
        "EMAIL": "kyoko.saito",
    },
    {
        "LAST_NAME": "佐々木",
        "FIRST_NAME": "久美",
        "LAST_NAME_KATAKANA": "ササキ",
        "FIRST_NAME_KATAKANA": "クミ",
        "EMAIL": "kumi,sasaki",
    },
    {
        "LAST_NAME": "佐々木",
        "FIRST_NAME": "美玲",
        "LAST_NAME_KATAKANA": "ササキ",
        "FIRST_NAME_KATAKANA": "ミレイ",
        "EMAIL": "mirei.sasaki",
    },
    {
        "LAST_NAME": "高瀬",
        "FIRST_NAME": "愛奈",
        "LAST_NAME_KATAKANA": "タカセ",
        "FIRST_NAME_KATAKANA": "マナ",
        "EMAIL": "mana.takase",
    },
    {
        "LAST_NAME": "高本",
        "FIRST_NAME": "彩花",
        "LAST_NAME_KATAKANA": "タカモト",
        "FIRST_NAME_KATAKANA": "アヤカ",
        "EMAIL": "ayaka.takamoto",
    },
    {
        "LAST_NAME": "東村",
        "FIRST_NAME": "芽依",
        "LAST_NAME_KATAKANA": "ヒガシムラ",
        "FIRST_NAME_KATAKANA": "メイ",
        "EMAIL": "mei.higashimura",
    },
    {
        "LAST_NAME": "金村",
        "FIRST_NAME": "美玖",
        "LAST_NAME_KATAKANA": "カネムラ",
        "FIRST_NAME_KATAKANA": "ミク",
        "EMAIL": "miku.kanemura",
    },
    {
        "LAST_NAME": "河田",
        "FIRST_NAME": "陽菜",
        "LAST_NAME_KATAKANA": "カワタ",
        "FIRST_NAME_KATAKANA": "ヒナ",
        "EMAIL": "hina.kawata",
    },
    {
        "LAST_NAME": "小坂",
        "FIRST_NAME": "菜緒",
        "LAST_NAME_KATAKANA": "コサカ",
        "FIRST_NAME_KATAKANA": "ナオ",
        "EMAIL": "nao.kosaka",
    },
    {
        "LAST_NAME": "富田",
        "FIRST_NAME": "鈴花",
        "LAST_NAME_KATAKANA": "トミタ",
        "FIRST_NAME_KATAKANA": "スズカ",
        "EMAIL": "suzuka.tomita",
    },
    {
        "LAST_NAME": "丹生",
        "FIRST_NAME": "明里",
        "LAST_NAME_KATAKANA": "ニブ",
        "FIRST_NAME_KATAKANA": "アカリ",
        "EMAIL": "akari.nibu",
    },
    {
        "LAST_NAME": "濱岸",
        "FIRST_NAME": "ひより",
        "LAST_NAME_KATAKANA": "ハマギシ",
        "FIRST_NAME_KATAKANA": "ヒヨリ",
        "EMAIL": "hiyori.hamagishi",
    },
    {
        "LAST_NAME": "松田",
        "FIRST_NAME": "好花",
        "LAST_NAME_KATAKANA": "マツダ",
        "FIRST_NAME_KATAKANA": "コノカ",
        "EMAIL": "konoka.matsuda",
    },
    {
        "LAST_NAME": "宮田",
        "FIRST_NAME": "愛萌",
        "LAST_NAME_KATAKANA": "ミヤタ",
        "FIRST_NAME_KATAKANA": "マナモ",
        "EMAIL": "manamo.miyata",
    },
    {
        "LAST_NAME": "渡邉",
        "FIRST_NAME": "美穂",
        "LAST_NAME_KATAKANA": "ワタナベ",
        "FIRST_NAME_KATAKANA": "ミホ",
        "EMAIL": "miho.watanabe",
    },
    {
        "LAST_NAME": "上村",
        "FIRST_NAME": "ひなの",
        "LAST_NAME_KATAKANA": "カミムラ",
        "FIRST_NAME_KATAKANA": "ヒナノ",
        "EMAIL": "hinano.kamimura",
    },
]
AUDREY = [
    {
        "LAST_NAME": "春日",
        "FIRST_NAME": "俊彰",
        "LAST_NAME_KATAKANA": "カスガ",
        "FIRST_NAME_KATAKANA": "トシアキ",
        "EMAIL": "toshiaki.kasuga",
    },
    {
        "LAST_NAME": "若林",
        "FIRST_NAME": "正恭",
        "LAST_NAME_KATAKANA": "ワカバヤシ",
        "FIRST_NAME_KATAKANA": "マサヤス",
        "EMAIL": "masayasu.wakabayashi",
    },
]
s = """#LAST_NAME #FIRST_NAME 様
この度はxxチケットをご利用いただき、ありがとうございます。
先日お申込みいただきました下記の抽選申込に関しまして、
厳正な抽選の結果、下記内容にてチケットをご用意いたしました。
以下にお支払い方法や、受取方法など大切なご案内を記載しております。
必ず最後までご確認をお願いいたします。

-----

■お名前カナ

#LAST_NAME_KATAKANA #FIRST_NAME_KATAKANA

■電話番号

#PHONE_NUMBER
 
■メールアドレス

#EMAIL

----- 
■受付番号

#RECEIPT_NUMBER
 
■受付日

2019年 3月 23日 18時 07分

---------------
【当選内容】 
■イベント名: 《先行》 欅坂46 3rd YEAR ANNIVERSARY LIVE [東京公演] 
■受付名称: ◇先行抽選[欅坂46FC] 
<第1希望> 
公演名:〔5/9(木)〕 欅坂46 3rd YEAR ANNIVERSARY LIVE 
公演日:2019年5月9日(木) 18:30 
会場:日本武道館 
申込内容 
指定席 ¥7,800 x 2枚 
チケット代金計:¥15,600 
システム利用料:¥432(税込) 
■お支払 
お支払方法: クレジットカード決済(抽選) 
※決済手数料:¥0/1枚ごと 
<クレジットカードでお支払いの方> 
ご指定のクレジットカードで、下記金額の決済を完了しました。 
決済合計金額:¥16,464(税込) 
※ご当選の場合、ご登録のクレジットカードにて決済完了となります。 
(引取手数料を含めた全額の決済となります)"""


def create_customer_list(customer_info: List[Dict[str, str]]) -> List[Dict[str, str]]:
    customer_list = [
        {
            "LAST_NAME": customer_dict["LAST_NAME"],
            "FIRST_NAME": customer_dict["FIRST_NAME"],
            "LAST_NAME_KATAKANA": customer_dict["LAST_NAME_KATAKANA"],
            "FIRST_NAME_KATAKANA": customer_dict["FIRST_NAME_KATAKANA"],
            "PHONE_NUMBER": f"{random.randint(0, 999):0^3}-{random.randint(0, 999):0^4}-{random.randint(0, 999):0^4}",
            "EMAIL": f"{customer_dict['EMAIL']}@example.com",
            "RECEIPT_NUMBER": f"{random.randint(0, 1000000):0^8}",
        }
        for customer_dict in customer_info
    ]
    return customer_list


def replace_variable(match: Match, customer: Dict[str, str]):
    m = match.group("variable")
    columns = create_customer_list(
        [
            {
                "LAST_NAME": "",
                "FIRST_NAME": "",
                "LAST_NAME_KATAKANA": "",
                "FIRST_NAME_KATAKANA": "",
                "EMAIL": "",
            },
        ]
    )[0].keys()
    for column in columns:
        if m == column:
            return f"{customer[column]}"


def main():
    customer_list = create_customer_list(HINATAZAKA + AUDREY)
    for customer in customer_list:
        inserted_text = REG_VARIABLE.sub(
            lambda wrapper: replace_variable(wrapper, customer), s
        )
        print(inserted_text)


if __name__ == "__main__":
    main()
```

### 【補足】re.subを使わずに高度な文字列置換を行う方法

1. 文字列をリストに変換する。
1. re.finditerでマッチした文字列と位置を取得する
1. イテレート中にリスト内の元の文字列の位置に挿入文字列と不足している空文字を挿入する
1. ''.join(リスト)で元の文字列に変換する（空文字はjoinで削除される）

```python

import random
import re
from typing import Dict, Pattern, List, Match

REG_VARIABLE: Pattern = re.compile(r"#(?P<variable>[A-Z|_]+)")
HINATAZAKA = [
    {
        "LAST_NAME": "井口",
        "FIRST_NAME": "眞緒",
        "LAST_NAME_KATAKANA": "イグチ",
        "FIRST_NAME_KATAKANA": "マオ",
        "EMAIL": "mao.iguchi",
    },
    {
        "LAST_NAME": "潮",
        "FIRST_NAME": "紗理菜",
        "LAST_NAME_KATAKANA": "ウシオ",
        "FIRST_NAME_KATAKANA": "マオ",
        "EMAIL": "sarina.ushio",
    },
    {
        "LAST_NAME": "影山",
        "FIRST_NAME": "優佳",
        "LAST_NAME_KATAKANA": "カゲヤマ",
        "FIRST_NAME_KATAKANA": "ユウカ",
        "EMAIL": "yuka.kageyama",
    },
    {
        "LAST_NAME": "加藤",
        "FIRST_NAME": "史帆",
        "LAST_NAME_KATAKANA": "カトウ",
        "FIRST_NAME_KATAKANA": "シホ",
        "EMAIL": "shiho,kato",
    },
    {
        "LAST_NAME": "齊藤",
        "FIRST_NAME": "京子",
        "LAST_NAME_KATAKANA": "サイトウ",
        "FIRST_NAME_KATAKANA": "キョウコ",
        "EMAIL": "kyoko.saito",
    },
    {
        "LAST_NAME": "佐々木",
        "FIRST_NAME": "久美",
        "LAST_NAME_KATAKANA": "ササキ",
        "FIRST_NAME_KATAKANA": "クミ",
        "EMAIL": "kumi,sasaki",
    },
    {
        "LAST_NAME": "佐々木",
        "FIRST_NAME": "美玲",
        "LAST_NAME_KATAKANA": "ササキ",
        "FIRST_NAME_KATAKANA": "ミレイ",
        "EMAIL": "mirei.sasaki",
    },
    {
        "LAST_NAME": "高瀬",
        "FIRST_NAME": "愛奈",
        "LAST_NAME_KATAKANA": "タカセ",
        "FIRST_NAME_KATAKANA": "マナ",
        "EMAIL": "mana.takase",
    },
    {
        "LAST_NAME": "高本",
        "FIRST_NAME": "彩花",
        "LAST_NAME_KATAKANA": "タカモト",
        "FIRST_NAME_KATAKANA": "アヤカ",
        "EMAIL": "ayaka.takamoto",
    },
    {
        "LAST_NAME": "東村",
        "FIRST_NAME": "芽依",
        "LAST_NAME_KATAKANA": "ヒガシムラ",
        "FIRST_NAME_KATAKANA": "メイ",
        "EMAIL": "mei.higashimura",
    },
    {
        "LAST_NAME": "金村",
        "FIRST_NAME": "美玖",
        "LAST_NAME_KATAKANA": "カネムラ",
        "FIRST_NAME_KATAKANA": "ミク",
        "EMAIL": "miku.kanemura",
    },
    {
        "LAST_NAME": "河田",
        "FIRST_NAME": "陽菜",
        "LAST_NAME_KATAKANA": "カワタ",
        "FIRST_NAME_KATAKANA": "ヒナ",
        "EMAIL": "hina.kawata",
    },
    {
        "LAST_NAME": "小坂",
        "FIRST_NAME": "菜緒",
        "LAST_NAME_KATAKANA": "コサカ",
        "FIRST_NAME_KATAKANA": "ナオ",
        "EMAIL": "nao.kosaka",
    },
    {
        "LAST_NAME": "富田",
        "FIRST_NAME": "鈴花",
        "LAST_NAME_KATAKANA": "トミタ",
        "FIRST_NAME_KATAKANA": "スズカ",
        "EMAIL": "suzuka.tomita",
    },
    {
        "LAST_NAME": "丹生",
        "FIRST_NAME": "明里",
        "LAST_NAME_KATAKANA": "ニブ",
        "FIRST_NAME_KATAKANA": "アカリ",
        "EMAIL": "akari.nibu",
    },
    {
        "LAST_NAME": "濱岸",
        "FIRST_NAME": "ひより",
        "LAST_NAME_KATAKANA": "ハマギシ",
        "FIRST_NAME_KATAKANA": "ヒヨリ",
        "EMAIL": "hiyori.hamagishi",
    },
    {
        "LAST_NAME": "松田",
        "FIRST_NAME": "好花",
        "LAST_NAME_KATAKANA": "マツダ",
        "FIRST_NAME_KATAKANA": "コノカ",
        "EMAIL": "konoka.matsuda",
    },
    {
        "LAST_NAME": "宮田",
        "FIRST_NAME": "愛萌",
        "LAST_NAME_KATAKANA": "ミヤタ",
        "FIRST_NAME_KATAKANA": "マナモ",
        "EMAIL": "manamo.miyata",
    },
    {
        "LAST_NAME": "渡邉",
        "FIRST_NAME": "美穂",
        "LAST_NAME_KATAKANA": "ワタナベ",
        "FIRST_NAME_KATAKANA": "ミホ",
        "EMAIL": "miho.watanabe",
    },
    {
        "LAST_NAME": "上村",
        "FIRST_NAME": "ひなの",
        "LAST_NAME_KATAKANA": "カミムラ",
        "FIRST_NAME_KATAKANA": "ヒナノ",
        "EMAIL": "hinano.kamimura",
    },
]
AUDREY = [
    {
        "LAST_NAME": "春日",
        "FIRST_NAME": "俊彰",
        "LAST_NAME_KATAKANA": "カスガ",
        "FIRST_NAME_KATAKANA": "トシアキ",
        "EMAIL": "toshiaki.kasuga",
    },
    {
        "LAST_NAME": "若林",
        "FIRST_NAME": "正恭",
        "LAST_NAME_KATAKANA": "ワカバヤシ",
        "FIRST_NAME_KATAKANA": "マサヤス",
        "EMAIL": "masayasu.wakabayashi",
    },
]
s = """#LAST_NAME #FIRST_NAME 様
この度はxxチケットをご利用いただき、ありがとうございます。
先日お申込みいただきました下記の抽選申込に関しまして、
厳正な抽選の結果、下記内容にてチケットをご用意いたしました。
以下にお支払い方法や、受取方法など大切なご案内を記載しております。
必ず最後までご確認をお願いいたします。

-----

■お名前カナ

#LAST_NAME_KATAKANA #FIRST_NAME_KATAKANA

■電話番号

#PHONE_NUMBER
 
■メールアドレス

#EMAIL

----- 
■受付番号

#RECEIPT_NUMBER
 
■受付日

2019年 3月 23日 18時 07分

---------------
【当選内容】 
■イベント名: 《先行》 欅坂46 3rd YEAR ANNIVERSARY LIVE [東京公演] 
■受付名称: ◇先行抽選[欅坂46FC] 
<第1希望> 
公演名:〔5/9(木)〕 欅坂46 3rd YEAR ANNIVERSARY LIVE 
公演日:2019年5月9日(木) 18:30 
会場:日本武道館 
申込内容 
指定席 ¥7,800 x 2枚 
チケット代金計:¥15,600 
システム利用料:¥432(税込) 
■お支払 
お支払方法: クレジットカード決済(抽選) 
※決済手数料:¥0/1枚ごと 
<クレジットカードでお支払いの方> 
ご指定のクレジットカードで、下記金額の決済を完了しました。 
決済合計金額:¥16,464(税込) 
※ご当選の場合、ご登録のクレジットカードにて決済完了となります。 
(引取手数料を含めた全額の決済となります)"""


def create_customer_list(customer_info: List[Dict[str, str]]) -> List[Dict[str, str]]:
    customer_list = [
        {
            "LAST_NAME": customer_dict["LAST_NAME"],
            "FIRST_NAME": customer_dict["FIRST_NAME"],
            "LAST_NAME_KATAKANA": customer_dict["LAST_NAME_KATAKANA"],
            "FIRST_NAME_KATAKANA": customer_dict["FIRST_NAME_KATAKANA"],
            "PHONE_NUMBER": f"{random.randint(0, 999):0^3}-{random.randint(0, 999):0^4}-{random.randint(0, 999):0^4}",
            "EMAIL": f"{customer_dict['EMAIL']}@example.com",
            "RECEIPT_NUMBER": f"{random.randint(0, 1000000):0^8}",
        }
        for customer_dict in customer_info
    ]
    return customer_list


def insert_text(content: str, customer: Dict[str, str], columns: List[str]) -> str:
    lst_content = list(content)
    for m in REG_VARIABLE.finditer(content):
        match_variable = m.group("variable")
        match_location = m.span()
        start, end = match_location[0], match_location[1]
        inserted_txt = replace_variable(match_variable, customer, columns)
        len_inserted_txt = len(inserted_txt)
        length = end - start
        lst_content[start : start + len_inserted_txt] = inserted_txt
        lst_content[match_location[0] + len_inserted_txt : match_location[1]] = [
            "" for _ in range(length - len_inserted_txt)
        ]
    return "".join(lst_content)


def replace_variable(match: Match, customer: Dict[str, str], columns: List[str]):
    for column in columns:
        if match == column:
            return customer[column]


def main():
    columns = create_customer_list(
        [
            {
                "LAST_NAME": "",
                "FIRST_NAME": "",
                "LAST_NAME_KATAKANA": "",
                "FIRST_NAME_KATAKANA": "",
                "EMAIL": "",
            },
        ]
    )[0].keys()
    customer_list = create_customer_list(HINATAZAKA + AUDREY)
    for customer in customer_list:
        inserted_text = insert_text(s, customer, columns)
        print(inserted_text)


if __name__ == "__main__":
    main()
```