【C言語】
CSV形式の解析方法
~sscanf関数の正規表現利用~

■この記事の概要

この記事は、C言語のsscanf関数を用いて、CSV形式の郵便番号データから情報を解析する方法を解説します。

コード例を通して住所や名前などのデータを抽出する方法を学べ、郵便番号から住所を検索する応用例も含まれています。


■基礎編:sscanfの正規表現でcsvを解析する方法

//csvを入力して構造体配列へ出力する
#include <stdio.h>
#include <stdlib.h>
char *csv[] = {
    "愛知,山田太郎,101",
    "岐阜,佐藤次郎,102",
    "三重,鈴木三郎,103",
    NULL
} ;
typedef struct {
    char    住所[1024];
    char    氏名[1024];
    int     年齢;
}       個人情報 ;
static  個人情報  個人情報配列[100];

static void     個人情報設定(char *csv[]){
    for(int i = 0; csv[i] != NULL;i++){
        個人情報 *p = &個人情報配列[i];
        //【住所,氏名,年齢】3フィールド読み込む
        int ret = sscanf(csv[i],
            "%[^,],%[^,],%d",//正規表現【カンマ以外,カンマ以外,数字】
            p->住所,
            p->氏名,
            &(p->年齢)
        );
        if(ret != 3){//3フィールド読めなかったら無視
            fprintf(stderr,"入力異常 ret=%d:%s",ret,csv[i]);
            continue ;
        }
    }
}
static void     個人情報表示(void){
    for(個人情報 *p = &個人情報配列[0]; p->氏名[0] != '\0' ;p++){
        printf("%s\t",p->住所);
        printf("%s\t",p->氏名);
        printf("%d\n",p->年齢);
    }
}
int     main(void){
    個人情報設定(csv);
    個人情報表示();
}

正規表現の
“%[^,],%[^,],%d”は
【カンマ以外,カンマ以外,数字】の
意味です。


応用編:郵便番号データベースから住所を求める

//
//  郵便番号から住所を表示するプログラム
//	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);
}

コピペしてコンパイル&実行してみて下さい。


参考:

郵便番号データダウンロード