■この記事の概要
この記事は、C言語のsscanf
関数を用いて、CSV形式の郵便番号データから情報を解析する方法を解説します。
コード例を通して住所や名前などのデータを抽出する方法を学べ、郵便番号から住所を検索する応用例も含まれています。
■基礎編:sscanfの正規表現でcsvを解析する方法
#include <stdio.h>
#include <stdlib.h>
//構造体の宣言
typedef struct 個人情報 {
char 住所[1024];
char 氏名[1024];
int 年齢;
} 個人情報 ;
//構造体配列の定義
static 個人情報 一覧[10];
//構造体ポインタを使った構造体のメンバ表示
static void 個人情報表示(個人情報 *p)
{
printf("■%s\n",p->住所);
printf("%s\n",p->氏名);
printf("%d\n",p->年齢);
}
static void 全個人情報表示(void)
{
for(個人情報 *p = &一覧[0]; p->氏名[0] != '\0' ;p++){
個人情報表示(p);
}
}
static void 全個人情報設定(FILE *fp)
{
個人情報 *p = &一覧[0];
char buf[BUFSIZ];
const int max = sizeof(一覧)/sizeof(一覧[0]);
while(fgets(buf,BUFSIZ,fp) != NULL){
//空行はスキップ
if(buf[0] == '\n'){
continue;
}
//入力データが多すぎないか確認
int id = p - &一覧[0];
if(id >= max){
fprintf(stderr,"最大値 %d 件を超えました\n",max);
exit(1);
}
//【住所,氏名,年齢】3フィールド読み込む
int ret = sscanf(buf,
"%[^,],%[^,],%d",//正規表現【カンマ以外,カンマ以外,数字】
p->住所,
p->氏名,
&(p->年齢)
);
if(ret != 3){//3フィールド読めなかったら無視
fprintf(stderr,"入力異常 ret=%d:%s",ret,buf);
continue ;
}
p++ ;
}
}
int main(void)
{
char *input = "input.csv";
FILE *fp = fopen(input,"r");
if(fp == NULL){
fprintf(stderr,"%s 開かん\n",input);
exit(1);
}
全個人情報設定(fp);
全個人情報表示();
fclose(fp);
}
コピペしてコンパイル&実行して動作を確認してください。
●input.csvの例
愛知,山田太郎,4
岐阜,佐藤次郎,5
三重,鈴木三郎,6
■応用編:郵便番号データベースから住所を求める
//
// 郵便番号から住所を表示するプログラム
// 2024/06/10 UTF版
// 作者:柴ONE
//
//https://www.post.japanpost.jp/zipcode/dl/utf-zip.html
//住所の郵便番号(1レコード1行、UTF-8形式)(CSV形式)
//最新データのダウンロード(zip形式)
//住所の郵便番号最新全データのダウンロードができます。
//最新データのダウンロード(zip形式:2.09Mバイト)
#if 0
https://www.post.japanpost.jp/zipcode/dl/utf-readme.html
郵便番号データファイルの形式等
この郵便番号データファイルでは、以下の順に配列しています。
1全国地方公共団体コード(JIS X0401、X0402)……… 半角数字
2(旧)郵便番号(5桁)……………………………………… 半角数字
■3郵便番号(7桁)……………………………………… 半角数字
4都道府県名 ………… 全角カタカナ(コード順に掲載) (※1)
5市区町村名 ………… 全角カタカナ(コード順に掲載) (※1)
6町域名 ……………… 全角カタカナ(五十音順に掲載) (※1)
■7都道府県名 ………… 漢字(コード順に掲載) (※1,2)
■8市区町村名 ………… 漢字(コード順に掲載) (※1,2)
■9町域名 ……………… 漢字(五十音順に掲載) (※1,2)
10一町域が二以上の郵便番号で表される場合の表示 (※3) (「1」は該当、「0」は該当せず)
11小字毎に番地が起番されている町域の表示 (※4) (「1」は該当、「0」は該当せず)
12丁目を有する町域の場合の表示 (「1」は該当、「0」は該当せず)
13一つの郵便番号で二以上の町域を表す場合の表示 (※5) (「1」は該当、「0」は該当せず)
14更新の表示(※6)(「0」は変更なし、「1」は変更あり、「2」廃止(廃止データのみ使用))
15変更理由 (「0」は変更なし、「1」市政・区政・町政・分区・政令指定都市施行、「2」住居表示の実施、「3」区画整理、「4」郵便区調整等、「5」訂正、「6」廃止(廃止データのみ使用))
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
void 郵便番号から住所表示(FILE *fp,char *郵便番号)
{
bool 検出 = false ;
char buf[BUFSIZ];
while (fgets(buf, BUFSIZ, fp) != NULL) {
char csv[10][128]; //csv[0]は捨てる
int ret = sscanf(buf,
"%[^,]," //csv[1] ,以外の単語,
"%[^,]," //csv[2]
"\"%[0-9]\"," //csv[3] "数字列",
"%[^,]," //csv[4]
"%[^,]," //csv[5]
"%[^,]," //csv[6]
"\"%[^,\"]\"," //csv[7] ",と\"以外の単語",
"\"%[^,\"]\"," //csv[8]
"\"%[^,\"]\"", //csv[9]
&csv[1][0],
&csv[2][0],
&csv[3][0], //郵便番号(7桁)
&csv[4][0],
&csv[5][0],
&csv[6][0],
&csv[7][0], //都道府県名
&csv[8][0], //市区町村名
&csv[9][0] //町域名
);
if(ret != 9){
fprintf(stderr,"形式異常 %s\n",buf);
continue;
}
if (strcmp(&csv[3][0], 郵便番号) != 0){
continue;
}
printf("%s➡住所:%s%s%s\n",
郵便番号,
&csv[7][0],
&csv[8][0],
&csv[9][0]
);
検出 = true;
break;
}
if(!検出){
fprintf(stderr,"郵便番号 %s は見つかりませんでした\n",郵便番号);
}
}
void 郵便番号入力(char *postnum)
{
printf("郵便番号をハイフンなしで入れてね: ");
int ret = scanf("%s",postnum);
if(ret != 1){
fprintf(stderr,"郵便番号異常\n");
exit(1);
}
if(strlen(postnum) != 7){
fprintf(stderr,"郵便番号異常 %zu桁 != 7桁(%s)\n",
strlen(postnum),
postnum);
exit(1);
}
}
FILE *郵便番号データファイル(void)
{
const char *csvfilename = "utf_ken_all/utf_ken_all.csv";
FILE *fp = fopen(csvfilename, "r");
if (fp == NULL){
fprintf(stderr,"%s 開かん\n",csvfilename);
exit(1);
}
return fp;
}
int main(void)
{
FILE *fp = 郵便番号データファイル();
char 郵便番号[128];
郵便番号入力(郵便番号);
郵便番号から住所表示(fp,郵便番号);
fclose(fp);
}
コピペしてコンパイル&実行してみて下さい。
参考: