日本語表記と英語表記を含む国名を分割する正規表現

前々回の下記投稿で法務省が公開している国名コード表(PDF)について紹介いたしました。

[Qiita@pipito.yukio「不正アクセスしてきたホストの国コードを知ってセキュリティ対策に活用する」](https://qiita.com/pipito-yukio/items/147f7b2f42ad9fc1dbd0)

法務省で公開している国名コード表のリンクは以下になります。

[法務省: 国名コード表 (PDF)](https://www.moj.go.jp/MINJI/common_igonsyo/pdf/001321964.pdf)

前々回の投稿で紹介したスクリプトは以下のようなものでした。

```bash
(py_psycopg2) $ python TestDetectCountryCode.py --target-ip 103.77.241.34
INFO Find 103.77.241.34 in (network: "103.77.240.0/23", country_code: "VN")
```

上記 **```country_code: "VN"```** はどの国？と国名コード表を開きその都度調べていましたがやっぱり不便。

**今回はダウンロードしたPDFの国名コード表からテキストを取り出し、テーブル用のCSVファイルを出力するための sed コマンドの正規表現について紹介します。**


国名コード表は全８ページに渡って199ヶ国分のデータが記録されています。
※コード表のスクリーンショットは著作権の問題がありますので内容の抜粋をテーブル形式で示します。

| コード | 国又は地域名（日本語表記） | 国又は地域名（英語表記） |
|:-|:-|:-|
| IS | アイスランド共和国 |Republic of Iceland |
| IE | アイルランド |Ireland |
| AZ | アゼルバイジャン共和国 |Republic of Azerbaijan |
| AF | アフガニスタン・イスラム共和国 |Islamic Republic of Afghanistan |
| US | アメリカ合衆国 |United States of America |
| AE | アラブ首長国連邦 |United Arab Emirates |
| DZ | アルジェリア民主人民共和国 |**People's Democratic Republic of Algeria** |
| - | 一部省略 | 一部省略 |
| GB | 英国（グレートブリテン及び北アイルランド連合王国） | United Kingdom of Great Britain and Northern Ireland |
| - | 一部省略 | 一部省略 |
| LB | レバノン共和国 |Lebanese Republic |
| RU | ロシア連邦 |Russian Federation |

選択した範囲をコピーしたテキストをファイル保存したもから一部抜粋を示します。
※1 Ubuntu Desktop で実行したところ区切りなしで出力されました。
**※2 英国（グレートブリテン及び北アイルランド連合王国）の行だけが日本語表記・英名表記が改行されてしまうので、この行のみ手作業で改行を削除しました。**
```country_code_table.txt
ISアイスランド共和国Republic of Iceland
IEアイルランドIreland
AZアゼルバイジャン共和国Republic of Azerbaijan
AFアフガニスタン・イスラム共和国Islamic Republic of Afghanistan
USアメリカ合衆国United States of America
AEアラブ首長国連邦United Arab Emirates
DZアルジェリア民主人民共和国People's Democratic Republic of Algeria
...一部省略...
GB英国（グレートブリテン及び北アイルランド連合王国）United Kingdom of Great Britain and Northern Ireland
...一部省略...
LBレバノン共和国Lebanese Republic
RUロシア連邦Russian Federation
```

**上記で「アルジェリア民主人民共和国」を示したのには理由があります。
英語表記の国名でアポストロフィ [ ' ] が含まれると、その列を抽出する正規表現に大きく影響するためです。**

シェルスクリプトが出力するCSVファイルの抜粋を下記に示します。

```country_code_name.csv
"IS","アイスランド共和国","Republic of Iceland"
"IE","アイルランド","Ireland"
"AZ","アゼルバイジャン共和国","Republic of Azerbaijan"
"AF","アフガニスタン・イスラム共和国","Islamic Republic of Afghanistan"
"US","アメリカ合衆国","United States of America"
"AE","アラブ首長国連邦","United Arab Emirates"
"DZ","アルジェリア民主人民共和国","People's Democratic Republic of Algeria"
...一部省略...
"GB","英国（グレートブリテン及び北アイルランド連合王国）","United Kingdom of Great Britain and Northern Ireland"
...一部省略...
"LB","レバノン共和国","Lebanese Republic"
"RU","ロシア連邦","Russian Federation"
```


### 1. 実行環境

+ OS: Ubuntu Desktop 22.04

bashのバージョン

```bash
$ LANG=C;bash --version
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
```

### 2. 参考サイトと参考書籍

#### 2-1. 参考サイト

+ [ITmedia @IT 【 sed 】コマンド（応用編その2）――ホールドスペースの活用](https://atmarkit.itmedia.co.jp/ait/articles/1806/07/news021.html)
非常にわかりやすく解説されています。いつも利用するお気に入りのサイトの一つです。

#### 2-2. 参考書籍

+ 「UNIX シェルスクリプト ▶逆引き大全◀　３３３の極意」
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/5b54a815-e645-2ef7-3c46-981c65afc404.jpeg">

発行日が 2006年9月10日 (第1版第5刷) と年代物ですが20年近く愛用しています。
**3-2 文字列を扱う, 5-5 テキストファイルの操作 [巻末] 付録 A.6 正規表現リファレンス**


### 3. CSV変換シェルスクリプト

#### 3-1. 行を３分割する正規表現

今回のデータでは２列目の日本語文字列部分はアルファベット以外で抽出可能です。

**３列目が一番のポイントで、BRE (基本正規表現) の文字列クラスを３つ並べることです。
※1 アポストロフィのリテラルは指定できません。
※2 アポストロフィは句読点クラスに含まれます。**

```
[1列目] [A-Z]{2}    ※国コードはアルファベットの大文字２文字
[2列目] [^A-Za-z]+  ※国又は地域名（日本語表記）は全角文字なのでアルファベット以外
[3列目] [[:alpha:][:space:][:punct:]  ※[アルファベット][スペース][句読点]
```

以下はアポストロフィのリテラルを指定したときのエラー
※アポストロフィのリテラルのエスケープ(バックスラッシュ)も同様です

```bash
# アポストロフィのリテラル
$ cat country_code_table.txt \
> | sed -rn 's/(^[A-Z]{2})([^A-Za-z]+)([A-Za-z ']+)/\1 \2 \3/p'
bash: 予期しないトークン `)' 周辺に構文エラーがあります

# アポストロフィのリテラルのエスケープ
$ cat country_code_table.txt \
> | sed -rn 's/(^[A-Z]{2})([^A-Za-z]+)([A-Za-z \']+)/\1 \2 \3/p'
bash: 予期しないトークン `)' 周辺に構文エラーがあります
```

#### 3-2. sed コマンドで正規表現を適用

３分割するコードスニペット
+ オブション指定
    + 拡拡張正規表現を使用するオプション: **```-r```**
    + **```-n```** オプションと **s コマンドの "p" オプション** で置換処理する


**```sed -rn 's/(^[A-Z]{2})([^A-Za-z]+)([[:alpha:][:space:][:punct:]]+$)/\1 \2 \3/p'```**

```
$ cat country_code_table.txt \
> | sed -rn 's/(^[A-Z]{2})([^A-Za-z]+)([[:alpha:][:space:][:punct:]]+$)/\1 \2 \3/p'
IS アイスランド共和国 Republic of Iceland
IE アイルランド Ireland
AZ アゼルバイジャン共和国 Republic of Azerbaijan
AF アフガニスタン・イスラム共和国 Islamic Republic of Afghanistan
US アメリカ合衆国 United States of America
AE アラブ首長国連邦 United Arab Emirates
DZ アルジェリア民主人民共和国 People's Democratic Republic of Algeria
# ...一部省略...
GB 英国（グレートブリテン及び北アイルランド連合王国） United Kingdom of Great Britain and Northern Ireland
# ...一部省略...
LB レバノン共和国 Lebanese Republic
RU ロシア連邦 Russian Federation
```

#### 3-3. CSV形式でファイル保存

PDFから抽出したテキストの全行を以下に示します。

```country_code_table.txt
ISアイスランド共和国Republic of Iceland
IEアイルランドIreland
AZアゼルバイジャン共和国Republic of Azerbaijan
AFアフガニスタン・イスラム共和国Islamic Republic of Afghanistan
USアメリカ合衆国United States of America
AEアラブ首長国連邦United Arab Emirates
DZアルジェリア民主人民共和国People's Democratic Republic of Algeria
ARアルゼンチン共和国Argentine Republic
ALアルバニア共和国Republic of Albania
AMアルメニア共和国Republic of Armenia
AOアンゴラ共和国Republic of Angola
AGアンティグア・バーブーダAntigua and Barbuda
ADアンドラ公国Principality of Andorra
YEイエメン共和国Republic of Yemen
ILイスラエル国State of Israel
ITイタリア共和国Italian Republic
IQイラク共和国Republic of Iraq
IRイラン・イスラム共和国Islamic Republic of Iran
INインドIndia
IDインドネシア共和国Republic of Indonesia
UGウガンダ共和国Republic of Uganda
UAウクライナUkraine
UZウズベキスタン共和国Republic of Uzbekistan
UYウルグアイ東方共和国Oriental Republic of Uruguay
GB英国（グレートブリテン及び北アイルランド連合王国）United Kingdom of Great Britain and Northern Ireland
ECエクアドル共和国Republic of Ecuador
EGエジプト・アラブ共和国Arab Republic of Egypt
EEエストニア共和国Republic of Estonia
SZエスワティニ王国Kingdom of Eswatini
ETエチオピア連邦民主共和国Federal Democratic Republic of Ethiopia
ERエリトリア国State of Eritrea
SVエルサルバドル共和国Republic of El Salvador
AUオーストラリア連邦Australia
ATオーストリア共和国Republic of Austria
OMオマーン国Sultanate of Oman
NLオランダ王国Kingdom of the Netherlands
GHガーナ共和国Republic of Ghana
CVカーボベルデ共和国Republic of Cabo Verde
GYガイアナ共和国Republic of Guyana
KZカザフスタン共和国Republic of Kazakhstan
QAカタール国State of Qatar
CAカナダCanada
GAガボン共和国Gabonese Republic
CMカメルーン共和国Republic of Cameroon
GMガンビア共和国Republic of The Gambia
KHカンボジア王国Kingdom of Cambodia
MK北マケドニア共和国Republic of North Macedonia
GNギニア共和国Republic of Guinea
GWギニアビサウ共和国Republic of Guinea-Bissau
CYキプロス共和国Republic of Cyprus
CUキューバ共和国Republic of Cuba
GRギリシャ共和国Hellenic Republic
KIキリバス共和国Republic of Kiribati
KGキルギス共和国Kyrgyz Republic
GTグアテマラ共和国Republic of Guatemala
KWクウェート国State of Kuwait
CKクック諸島Cook Islands
GDグレナダGrenada
HRクロアチア共和国Republic of Croatia
KEケニア共和国Republic of Kenya
CIコートジボワール共和国Republic of Cote d'Ivoire
CRコスタリカ共和国Republic of Costa Rica
KMコモロ連合Union of Comoros
COコロンビア共和国Republic of Colombia
CGコンゴ共和国Republic of Congo
CDコンゴ民主共和国Democratic Republic of the Congo
SAサウジアラビア王国Kingdom of Saudi Arabia
WSサモア独立国Independent State of Samoa
STサントメ・プリンシペ民主共和国Democratic Republic of Sao Tome and Principe
ZMザンビア共和国Republic of Zambia
SMサンマリノ共和国Republic of San Marino
SLシエラレオネ共和国Republic of Sierra Leone
DJジブチ共和国Republic of Djibouti
JMジャマイカJamaica
GEジョージアGeorgia
SYシリア・アラブ共和国Syrian Arab Republic
SGシンガポール共和国Republic of Singapore
ZWジンバブエ共和国Republic of Zimbabwe
CHスイス連邦Swiss Confederation
SEスウェーデン王国Kingdom of Sweden
SDスーダン共和国The Republic of the Sudan
ESスペイン王国Kingdom of Spain
SRスリナム共和国Republic of Suriname
LKスリランカ民主社会主義共和国Democratic Socialist Republic of Sri Lanka
SKスロバキア共和国Slovak Republic
SIスロベニア共和国Republic of Slovenia
SCセーシェル共和国Republic of Seychelles
GQ赤道ギニア共和国Republic of Equatorial Guinea
SNセネガル共和国Republic of Senegal
RSセルビア共和国Republic of Serbia
KNセントクリストファー・ネービスSaint Christopher and Nevis
VCセントビンセント及びグレナディーン諸島Saint Vincent and the Grenadines
LCセントルシアSaint Lucia
SOソマリア連邦共和国Federal Republic of Somalia
SBソロモン諸島Solomon Islands
THタイ王国Kingdom of Thailand
KR大韓民国Republic of Korea
TW台湾Taiwan
TJタジキスタン共和国Republic of Tajikistan
TZタンザニア連合共和国United Republic of Tanzania
CZチェコ共和国Czech Republic
TDチャド共和国Republic of Chad
CF中央アフリカ共和国Central African Republic
CN中華人民共和国People's Republic of China
TNチュニジア共和国Republic of Tunisia
KP北朝鮮North Korea
CLチリ共和国Republic of Chile
TVツバルTuvalu
DKデンマーク王国Kingdom of Denmark
DEドイツ連邦共和国Federal Republic of Germany
TGトーゴ共和国Republic of Togo
DOドミニカ共和国Dominican Republic
DMドミニカ国Commonwealth of Dominica
TTトリニダード・トバゴ共和国Republic of Trinidad and Tobago
TMトルクメニスタンTurkmenistan
TRトルコ共和国Republic of Turkey
TOトンガ王国Kingdom of Tonga
NGナイジェリア連邦共和国Federal Republic of Nigeria
NRナウル共和国Republic of Nauru
NAナミビア共和国Republic of Namibia
NUニウエNiue
NIニカラグア共和国Republic of Nicaragua
NEニジェール共和国Republic of Niger
NZニュージーランドNew Zealand
NPネパール連邦民主共和国Federal Democratic Republic of Nepal
NOノルウェー王国Kingdom of Norway
BHバーレーン王国Kingdom of Bahrain
HTハイチ共和国Republic of Haiti
PKパキスタン・イスラム共和国Islamic Republic of Pakistan
VAバチカンVatican
PAパナマ共和国Republic of Panama
VUバヌアツ共和国Republic of Vanuatu
BSバハマ国Commonwealth of The Bahamas
PGパプアニューギニア独立国Independent State of Papua New Guinea
PWパラオ共和国Republic of Palau
PYパラグアイ共和国Republic of Paraguay
BBバルバドスBarbados
PSパレスチナPalestine
HUハンガリーHungary
BDバングラデシュ人民共和国People's Republic of Bangladesh
TL東ティモール民主共和国The Democratic Republic of Timor-Leste
FJフィジー共和国Republic of Fiji
PHフィリピン共和国Republic of the Philippines
FIフィンランド共和国Republic of Finland
BTブータン王国Kingdom of Bhutan
BRブラジル連邦共和国Federative Republic of Brazil
FRフランス共和国French Republic
BGブルガリア共和国Republic of Bulgaria
BFブルキナファソBurkina Faso
BNブルネイ・ダルサラーム国Brunei Darussalam
BIブルンジ共和国Republic of Burundi
VNベトナム社会主義共和国Socialist Republic of Viet Nam
BJベナン共和国Republic of Benin
VEベネズエラ・ボリバル共和国Bolivarian Republic of Venezuela
BYベラルーシ共和国Republic of Belarus
BZベリーズBelize
PEペルー共和国Republic of Peru
BEベルギー王国Kingdom of Belgium
PLポーランド共和国Republic of Poland
BAボスニア・ヘルツェゴビナBosnia and Herzegovina
BWボツワナ共和国Republic of Botswana
BOボリビア多民族国Plurinational State of Bolivia
PTポルトガル共和国Portuguese Republic
HK香港Hong Kong
HNホンジュラス共和国Republic of Honduras
MHマーシャル諸島共和国Republic of the Marshall Islands
MOマカオMacau
MGマダガスカル共和国Republic of Madagascar
MWマラウイ共和国Republic of Malawi
MLマリ共和国Republic of Mali
MTマルタ共和国Republic of Malta
MYマレーシアMalaysia
FMミクロネシア連邦Federated States of Micronesia
ZA南アフリカ共和国Republic of South Africa
SS南スーダン共和国The Republic of South Sudan
MMミャンマー連邦共和国Republic of the Union of Myanmar
MXメキシコ合衆国United Mexican States
MUモーリシャス共和国Republic of Mauritius
MRモーリタニア・イスラム共和国Islamic Republic of Mauritania
MZモザンビーク共和国Republic of Mozambique
MCモナコ公国Principality of Monaco
MVモルディブ共和国Republic of Maldives
MDモルドバ共和国Republic of Moldova
MAモロッコ王国Kingdom of Morocco
MNモンゴル国Mongolia
MEモンテネグロMontenegro
JOヨルダンJordan
LAラオス人民民主共和国Lao People's Democratic Republic
LVラトビア共和国Republic of Latvia
LTリトアニア共和国Republic of Lithuania
LYリビアLibya
LIリヒテンシュタイン公国Principality of Liechtenstein
LRリベリア共和国Republic of Liberia
ROルーマニアRomania
LUルクセンブルク大公国Grand Duchy of Luxembourg
RWルワンダ共和国Republic of Rwanda
LSレソト王国Kingdom of Lesotho
LBレバノン共和国Lebanese Republic
RUロシア連邦Russian Federation
```

(1) ヘッダーをファイル保存

```bash
$ echo "\"country_code\",\"japanese_name\",\"public_name\"" > country_code_name.csv
```

(2) CSV形式でファイルに追記する

```bash
$ cat country_code_table.txt \
> | sed -rn 's/(^[A-Z]{2})([^A-Za-z]+)([[:alpha:][:space:][:punct:]]+$)/"\1","\2","\3"/p' \
> >>country_code_name.csv
```

(3)出力された内容

```country_code_name.csv
"country_code","japanese_name","public_name"
"IS","アイスランド共和国","Republic of Iceland"
"IE","アイルランド","Ireland"
"AZ","アゼルバイジャン共和国","Republic of Azerbaijan"
"AF","アフガニスタン・イスラム共和国","Islamic Republic of Afghanistan"
"US","アメリカ合衆国","United States of America"
"AE","アラブ首長国連邦","United Arab Emirates"
"DZ","アルジェリア民主人民共和国","People's Democratic Republic of Algeria"
"AR","アルゼンチン共和国","Argentine Republic"
"AL","アルバニア共和国","Republic of Albania"
"AM","アルメニア共和国","Republic of Armenia"
"AO","アンゴラ共和国","Republic of Angola"
"AG","アンティグア・バーブーダ","Antigua and Barbuda"
"AD","アンドラ公国","Principality of Andorra"
"YE","イエメン共和国","Republic of Yemen"
"IL","イスラエル国","State of Israel"
"IT","イタリア共和国","Italian Republic"
"IQ","イラク共和国","Republic of Iraq"
"IR","イラン・イスラム共和国","Islamic Republic of Iran"
"IN","インド","India"
"ID","インドネシア共和国","Republic of Indonesia"
"UG","ウガンダ共和国","Republic of Uganda"
"UA","ウクライナ","Ukraine"
"UZ","ウズベキスタン共和国","Republic of Uzbekistan"
"UY","ウルグアイ東方共和国","Oriental Republic of Uruguay"
"GB","英国（グレートブリテン及び北アイルランド連合王国）","United Kingdom of Great Britain and Northern Ireland"
"EC","エクアドル共和国","Republic of Ecuador"
"EG","エジプト・アラブ共和国","Arab Republic of Egypt"
"EE","エストニア共和国","Republic of Estonia"
"SZ","エスワティニ王国","Kingdom of Eswatini"
"ET","エチオピア連邦民主共和国","Federal Democratic Republic of Ethiopia"
"ER","エリトリア国","State of Eritrea"
"SV","エルサルバドル共和国","Republic of El Salvador"
"AU","オーストラリア連邦","Australia"
"AT","オーストリア共和国","Republic of Austria"
"OM","オマーン国","Sultanate of Oman"
"NL","オランダ王国","Kingdom of the Netherlands"
"GH","ガーナ共和国","Republic of Ghana"
"CV","カーボベルデ共和国","Republic of Cabo Verde"
"GY","ガイアナ共和国","Republic of Guyana"
"KZ","カザフスタン共和国","Republic of Kazakhstan"
"QA","カタール国","State of Qatar"
"CA","カナダ","Canada"
"GA","ガボン共和国","Gabonese Republic"
"CM","カメルーン共和国","Republic of Cameroon"
"GM","ガンビア共和国","Republic of The Gambia"
"KH","カンボジア王国","Kingdom of Cambodia"
"MK","北マケドニア共和国","Republic of North Macedonia"
"GN","ギニア共和国","Republic of Guinea"
"GW","ギニアビサウ共和国","Republic of Guinea-Bissau"
"CY","キプロス共和国","Republic of Cyprus"
"CU","キューバ共和国","Republic of Cuba"
"GR","ギリシャ共和国","Hellenic Republic"
"KI","キリバス共和国","Republic of Kiribati"
"KG","キルギス共和国","Kyrgyz Republic"
"GT","グアテマラ共和国","Republic of Guatemala"
"KW","クウェート国","State of Kuwait"
"CK","クック諸島","Cook Islands"
"GD","グレナダ","Grenada"
"HR","クロアチア共和国","Republic of Croatia"
"KE","ケニア共和国","Republic of Kenya"
"CI","コートジボワール共和国","Republic of Cote d'Ivoire"
"CR","コスタリカ共和国","Republic of Costa Rica"
"KM","コモロ連合","Union of Comoros"
"CO","コロンビア共和国","Republic of Colombia"
"CG","コンゴ共和国","Republic of Congo"
"CD","コンゴ民主共和国","Democratic Republic of the Congo"
"SA","サウジアラビア王国","Kingdom of Saudi Arabia"
"WS","サモア独立国","Independent State of Samoa"
"ST","サントメ・プリンシペ民主共和国","Democratic Republic of Sao Tome and Principe"
"ZM","ザンビア共和国","Republic of Zambia"
"SM","サンマリノ共和国","Republic of San Marino"
"SL","シエラレオネ共和国","Republic of Sierra Leone"
"DJ","ジブチ共和国","Republic of Djibouti"
"JM","ジャマイカ","Jamaica"
"GE","ジョージア","Georgia"
"SY","シリア・アラブ共和国","Syrian Arab Republic"
"SG","シンガポール共和国","Republic of Singapore"
"ZW","ジンバブエ共和国","Republic of Zimbabwe"
"CH","スイス連邦","Swiss Confederation"
"SE","スウェーデン王国","Kingdom of Sweden"
"SD","スーダン共和国","The Republic of the Sudan"
"ES","スペイン王国","Kingdom of Spain"
"SR","スリナム共和国","Republic of Suriname"
"LK","スリランカ民主社会主義共和国","Democratic Socialist Republic of Sri Lanka"
"SK","スロバキア共和国","Slovak Republic"
"SI","スロベニア共和国","Republic of Slovenia"
"SC","セーシェル共和国","Republic of Seychelles"
"GQ","赤道ギニア共和国","Republic of Equatorial Guinea"
"SN","セネガル共和国","Republic of Senegal"
"RS","セルビア共和国","Republic of Serbia"
"KN","セントクリストファー・ネービス","Saint Christopher and Nevis"
"VC","セントビンセント及びグレナディーン諸島","Saint Vincent and the Grenadines"
"LC","セントルシア","Saint Lucia"
"SO","ソマリア連邦共和国","Federal Republic of Somalia"
"SB","ソロモン諸島","Solomon Islands"
"TH","タイ王国","Kingdom of Thailand"
"KR","大韓民国","Republic of Korea"
"TW","台湾","Taiwan"
"TJ","タジキスタン共和国","Republic of Tajikistan"
"TZ","タンザニア連合共和国","United Republic of Tanzania"
"CZ","チェコ共和国","Czech Republic"
"TD","チャド共和国","Republic of Chad"
"CF","中央アフリカ共和国","Central African Republic"
"CN","中華人民共和国","People's Republic of China"
"TN","チュニジア共和国","Republic of Tunisia"
"KP","北朝鮮","North Korea"
"CL","チリ共和国","Republic of Chile"
"TV","ツバル","Tuvalu"
"DK","デンマーク王国","Kingdom of Denmark"
"DE","ドイツ連邦共和国","Federal Republic of Germany"
"TG","トーゴ共和国","Republic of Togo"
"DO","ドミニカ共和国","Dominican Republic"
"DM","ドミニカ国","Commonwealth of Dominica"
"TT","トリニダード・トバゴ共和国","Republic of Trinidad and Tobago"
"TM","トルクメニスタン","Turkmenistan"
"TR","トルコ共和国","Republic of Turkey"
"TO","トンガ王国","Kingdom of Tonga"
"NG","ナイジェリア連邦共和国","Federal Republic of Nigeria"
"NR","ナウル共和国","Republic of Nauru"
"NA","ナミビア共和国","Republic of Namibia"
"NU","ニウエ","Niue"
"NI","ニカラグア共和国","Republic of Nicaragua"
"NE","ニジェール共和国","Republic of Niger"
"NZ","ニュージーランド","New Zealand"
"NP","ネパール連邦民主共和国","Federal Democratic Republic of Nepal"
"NO","ノルウェー王国","Kingdom of Norway"
"BH","バーレーン王国","Kingdom of Bahrain"
"HT","ハイチ共和国","Republic of Haiti"
"PK","パキスタン・イスラム共和国","Islamic Republic of Pakistan"
"VA","バチカン","Vatican"
"PA","パナマ共和国","Republic of Panama"
"VU","バヌアツ共和国","Republic of Vanuatu"
"BS","バハマ国","Commonwealth of The Bahamas"
"PG","パプアニューギニア独立国","Independent State of Papua New Guinea"
"PW","パラオ共和国","Republic of Palau"
"PY","パラグアイ共和国","Republic of Paraguay"
"BB","バルバドス","Barbados"
"PS","パレスチナ","Palestine"
"HU","ハンガリー","Hungary"
"BD","バングラデシュ人民共和国","People's Republic of Bangladesh"
"TL","東ティモール民主共和国","The Democratic Republic of Timor-Leste"
"FJ","フィジー共和国","Republic of Fiji"
"PH","フィリピン共和国","Republic of the Philippines"
"FI","フィンランド共和国","Republic of Finland"
"BT","ブータン王国","Kingdom of Bhutan"
"BR","ブラジル連邦共和国","Federative Republic of Brazil"
"FR","フランス共和国","French Republic"
"BG","ブルガリア共和国","Republic of Bulgaria"
"BF","ブルキナファソ","Burkina Faso"
"BN","ブルネイ・ダルサラーム国","Brunei Darussalam"
"BI","ブルンジ共和国","Republic of Burundi"
"VN","ベトナム社会主義共和国","Socialist Republic of Viet Nam"
"BJ","ベナン共和国","Republic of Benin"
"VE","ベネズエラ・ボリバル共和国","Bolivarian Republic of Venezuela"
"BY","ベラルーシ共和国","Republic of Belarus"
"BZ","ベリーズ","Belize"
"PE","ペルー共和国","Republic of Peru"
"BE","ベルギー王国","Kingdom of Belgium"
"PL","ポーランド共和国","Republic of Poland"
"BA","ボスニア・ヘルツェゴビナ","Bosnia and Herzegovina"
"BW","ボツワナ共和国","Republic of Botswana"
"BO","ボリビア多民族国","Plurinational State of Bolivia"
"PT","ポルトガル共和国","Portuguese Republic"
"HK","香港","Hong Kong"
"HN","ホンジュラス共和国","Republic of Honduras"
"MH","マーシャル諸島共和国","Republic of the Marshall Islands"
"MO","マカオ","Macau"
"MG","マダガスカル共和国","Republic of Madagascar"
"MW","マラウイ共和国","Republic of Malawi"
"ML","マリ共和国","Republic of Mali"
"MT","マルタ共和国","Republic of Malta"
"MY","マレーシア","Malaysia"
"FM","ミクロネシア連邦","Federated States of Micronesia"
"ZA","南アフリカ共和国","Republic of South Africa"
"SS","南スーダン共和国","The Republic of South Sudan"
"MM","ミャンマー連邦共和国","Republic of the Union of Myanmar"
"MX","メキシコ合衆国","United Mexican States"
"MU","モーリシャス共和国","Republic of Mauritius"
"MR","モーリタニア・イスラム共和国","Islamic Republic of Mauritania"
"MZ","モザンビーク共和国","Republic of Mozambique"
"MC","モナコ公国","Principality of Monaco"
"MV","モルディブ共和国","Republic of Maldives"
"MD","モルドバ共和国","Republic of Moldova"
"MA","モロッコ王国","Kingdom of Morocco"
"MN","モンゴル国","Mongolia"
"ME","モンテネグロ","Montenegro"
"JO","ヨルダン","Jordan"
"LA","ラオス人民民主共和国","Lao People's Democratic Republic"
"LV","ラトビア共和国","Republic of Latvia"
"LT","リトアニア共和国","Republic of Lithuania"
"LY","リビア","Libya"
"LI","リヒテンシュタイン公国","Principality of Liechtenstein"
"LR","リベリア共和国","Republic of Liberia"
"RO","ルーマニア","Romania"
"LU","ルクセンブルク大公国","Grand Duchy of Luxembourg"
"RW","ルワンダ共和国","Republic of Rwanda"
"LS","レソト王国","Kingdom of Lesotho"
"LB","レバノン共和国","Lebanese Republic"
"RU","ロシア連邦","Russian Federation"
```


### 4. CSVファイルの活用

参考までにテーブルにインポートする方法とCSVファイルをpythonスクリプトで利用する方法を紹介します。

dockerコンテナ、データベース、python環境等については下記投稿をご覧ください。

[Qiita@pipito.yukio「不正アクセスしてきたホストの国コードを知ってセキュリティ対策に活用する」](https://qiita.com/pipito-yukio/items/147f7b2f42ad9fc1dbd0)


#### 4-1. 国コード名称マスタテーブルへのインポート

PostgreSQLデータベース

(1) テーブル定義

```17_create_country_code_name_mst.sql
-- 国コードと和名国名、英語名マスター
CREATE TABLE mainte2.country_code_name_mst(
   country_code CHAR(2) PRIMARY KEY,
   japanese_name VARCHAR(32) NOT NULL,
   public_name VARCHAR(128) NOT NULL
);
ALTER TABLE mainte2.country_code_name_mst OWNER TO developer;
```

(2) テーブルの生成

```bash
$ docker exec -it postgres-qiita bash
1e2b0f7bc9c8:/# cd /home/qiita/data/sql/exmapledb
1e2b0f7bc9c8:/home/qiita/data/sql/exmapledb# psql -Udeveloper qiita_exampledb < 17_create_country_code_name_mst.sql 
CREATE TABLE
ALTER TABLE
```

(3) CSVファイルをテーブルにインポートする

```bash
1e2b0f7bc9c8:/home/qiita/data/sql/exmapledb# psql -Udeveloper qiita_exampledb \
> -c "\copy mainte2.country_code_name_mst FROM '/home/qiita/data/sql/exmapledb/csv/country_code_name.csv' DELIMITER ',' CSV HEADER;"
COPY 199
```

(4) インポート結果の確認

```bash
1e2b0f7bc9c8:/home/qiita/data/sql/exmapledb# echo "SELECT * FROM mainte2.country_code_name_mst;" \
> | psql -Udeveloper qiita_exampledb
 country_code |                   japanese_name                    |                     public_name                      
--------------+----------------------------------------------------+------------------------------------------------------
 IS           | アイスランド共和国                                 | Republic of Iceland
 IE           | アイルランド                                       | Ireland
 AZ           | アゼルバイジャン共和国                             | Republic of Azerbaijan
 AF           | アフガニスタン・イスラム共和国                     | Islamic Republic of Afghanistan
 US           | アメリカ合衆国                                     | United States of America
#...以下省略...
```

#### 4-2. pythonスクリプトで利用

機能を追加した部分のコードとメイン処理のみ示します。

##### 4-2-1. 機能追加後のコード

(1) CSVファイル読込みと辞書オブジェクト作成

```python
# 追加したインポートのみ
import csv
from typing import Dict, List, Iterator, Optional, Tuple


def read_csv(file_name: str,
             skip_header=True, header_cnt=1) -> List[str]:
    with open(file_name, 'r') as fp:
        reader = csv.reader(fp, dialect='unix')
        if skip_header:
            for skip in range(header_cnt):
                next(reader)
        # リストをカンマ区切りで連結する
        csv_lines = [",".join(rec) for rec in reader]
    return csv_lines


def read_cc_name_csv_todict(f_path: str) -> Dict[str, str]:
    result: Dict[str, str] = dict()
    lines: List[str] = read_csv(f_path)
    # ヘッダー: "country_code","japanese_name","public_name"
    for line in lines:
        fileds: List[str] = line.split(",")
        # 国コード: 日本語表記
        result[fileds[0]] = fileds[1]
    return result
```

(2) メイン処理 ★ コメントの有るところが追加した箇所

+ 入力パラメータの設定
    + **```--cc-name-csv```**: 国コードの国名CSVファイルパス ※任意
    (A) 指定されていれば国コードから国名(日本語表記)を出力に付け加える
    (B) 未指定の場合は従来の出力

```TestDetectCountryCode.py
# ...既存コード割愛...

def exec_main():
    logging.basicConfig(format='%(levelname)s %(message)s')
    app_logger = logging.getLogger(__name__)
    app_logger.setLevel(level=logging.DEBUG)
    parser = argparse.ArgumentParser()
    parser.add_argument("--target-ip", required=True, type=str,
                        help="IP address.")
    parser.add_argument("--enable-debug", action="store_true",
                        help="Enable logger debug out.")
    # ★ 国名コードと国名称CSVファイル ※任意
    parser.add_argument("--cc-name-csv", type=str,
                        help="Country code name CSV file.")
    args: argparse.Namespace = parser.parse_args()
    target_ip: str = args.target_ip
    enable_debug: bool = args.enable_debug

    # ★ 国名コードと国名CSVファイルが指定されている場合はファイル存在チェック
    dict_cc_name: Optional[Dict[str, str]] = None
    cc_name_csv_file: Optional[str] = args.cc_name_csv
    if cc_name_csv_file is not None:
        if not os.path.exists(cc_name_csv_file):
            app_logger.warning(f"{cc_name_csv_file} not found!")
            exit(1)

        # ★ CSVファイル読み込み
        dict_cc_name = read_cc_name_csv_todict(cc_name_csv_file)

    db: Optional[pgdatabase.PgDatabase] = None
    try:
        db = pgdatabase.PgDatabase(DB_CONF_FILE, logger=None)
        conn: connection = db.get_connection()
        matches: Optional[List[Tuple[str, int, str]]] = get_matches_main(
            conn, target_ip, logger=app_logger if enable_debug else None)
    except psycopg2.Error as db_err:
        app_logger.error(db_err)
        exit(1)
    except Exception as exp:
        app_logger.error(exp)
        exit(1)
    finally:
        if db is not None:
            db.close()

    # ターゲットIPのネットワーク(CIDR表記)と国コードを取得する
    if matches is not None and len(matches) > 0:
        network: Optional[str]
        cc: Optional[str]
        network, cc = detect_cc_in_matches(
            target_ip, matches, logger=app_logger if enable_debug else None)
        if network is not None and cc is not None:
            # ★ 国名コードと国名(日本語表記) 辞書オブジェクトが存在したら国名(日本語表記)を取得
            jp_name: Optional[str] = None
            if dict_cc_name is not None:
                jp_name = dict_cc_name.get(cc)
            if jp_name is not None:
                # ★ 国コードと国名(日本語表記)を出力
                app_logger.info(
                    f'Find {target_ip} in (network: "{network}", "{cc}:{jp_name}")'
                )
            else:
                app_logger.info(
                    f'Find {target_ip} in (network: "{network}", country_code: "{cc}")'
                )
        else:
            app_logger.info(f"Not match in data.")
    else:
        # このケースは想定しない
        app_logger.warning(f"Not exists in RIR table.")


if __name__ == '__main__':
    exec_main()
```

##### 4-2-2. スクリプト実行

(1) CSVファイルを指定

```bash
(py_psycopg2) $ python TestDetectCountryCode.py \
> --target-ip 103.77.241.34 --cc-name-csv csv/country_code_name.csv 
INFO Find 103.77.241.34 in (network: "103.77.240.0/23", "VN:ベトナム社会主義共和国")
```

(2) CSVファイル指定無し

```bash
(py_psycopg2) $ python TestDetectCountryCode.py \
> --target-ip 103.77.241.34
INFO Find 103.77.241.34 in (network: "103.77.240.0/23", country_code: "VN")
```

pythonスクリプトの機能追加版とCSVファイルは下記 GitHub リポジトリで公開しています。

[(GitHub) pipito-yukio / qiita-posts / python / Network_cc_in_target](https://github.com/pipito-yukio/qiita-posts/tree/main/python/Network_cc_in_target)

最新盤のpythonスクリプトと追加したリソースの一覧

```
Network_cc_in_target/
├── sql
│   └── 17_create_country_code_name_mst.sql # テーブル作成SQL
└── src
    ├── TestDetectCountryCode.py   # 最新版に更新しています
    └── csv
        └── country_code_name.csv  # 国コード国名CSVファイル
```