C++实现身份证号码过滤与排序

本文介绍了一个身份证信息解析与排序的程序实现。程序能够读取一批身份证号码,验证其有效性,并按出生日期从大到小排序输出。同时,文章提供了完整的源代码及编译说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.描述

警察办案里检索到一批(n个)身份证号码,希望按出生日期对它们进行从大到小排序,如果有相同日期,则按身份证号码大小进行排序,如果是错误的身份证号,则从排序列表中删除(仅需判断前两位省级地区编码是否在下面的列表中,以及对出生年月是否在1949.10.1-2022.10.1之间进行判断)。
备注:身份证号码为18位的数字组成,省级地区编码是第1到第2位,出生日期为第7到第14位。
身份证前两位各省对应的编号是:
11 北京市\12 天津市\13 河北省\14 山西省\15 内蒙古自治区
21 辽宁省\22 吉林省\23 黑龙江省
31 上海市\32 江苏省\33 浙江省\34 安徽省\35 福建省\36 江西省\37 山东省
41 河南省\42 湖北省\43 湖南省\44 广东省\45 广西壮族自治区\46 海南省
50 重庆市51 四川省52 贵州省53 云南省54 西藏自治区
61 陕西省62 甘肃省63 青海省64 宁夏回族自治区65 新疆维吾尔自治区
71 台湾省
81 香港特别行政区82 澳门特别行政区


*输入格式
第一行一个整数n,表示有n个身份证号码 (n<100)
余下的n行,每行一个身份证号码。 (已保证输入数字是18位)

*输出格式
按出生时间从大到小排序后的身份证号,每行一条 

*样例输入
8
466272199203271156 
21585619900709197X
21585620000228197X
234804198701078365
404475727700034980//【注:地区码不对】
710351199203313165
118698189201011234//【注:出生年份不对】
50123419831343135X//【注:出生月日不对】

*样例输出
234804198701078365
21585619900709197X
710351199203313165
466272199203271156

//////////////////////////

2.代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <bitset>
#include <string>
#include <vector>
#include <algorithm>
// 使用脚本make.sh编译
/*
(仅需判断前两位省级地区编码是否在下面的列表中,以及对出生年月是否在1949.10.1-2022.10.1之间进行判断)。
备注:身份证号码为18位的数字组成,省级地区编码是第1到第2位,出生日期为第7到第14位。
身份证前两位各省对应的编号是:
11 北京市\12 天津市\13 河北省\14 山西省\15 内蒙古自治区
21 辽宁省\22 吉林省\23 黑龙江省
31 上海市\32 江苏省\33 浙江省\34 安徽省\35 福建省\36 江西省\37 山东省
41 河南省\42 湖北省\43 湖南省\44 广东省\45 广西壮族自治区\46 海南省
50 重庆市51 四川省52 贵州省53 云南省54 西藏自治区
61 陕西省62 甘肃省63 青海省64 宁夏回族自治区65 新疆维吾尔自治区
71 台湾省
81 香港特别行政区82 澳门特别行政区
*/

/*
描述
C 库函数 int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。

声明
下面是 atoi() 函数的声明。

int atoi(const char *str)
参数
str -- 要转换为整数的字符串。
返回值
该函数返回转换后的长整数,如果没有执行有效的转换,则返回零。

*/
static const int ID_LEN = 18;
static const int YEAR_MIN = 1949;
static const int YEAR_MAX = 2022;
static const std::bitset<83>PROVINCE_CODES("11000000000100000111110000001111100011111100011111110000000111000001111100000000000");
struct id_info {
	id_info(const char *str) : id(str) {
	}
	bool parse_info() {
		if (id.size() != 18) {
			std::cerr << "error id size:" << id.size() << std::endl;
			return false;
		}
		code = (id[0] - '0') * 10 + id[1] - '0';
		if (code >= PROVINCE_CODES.size() || (false == PROVINCE_CODES.test(code))) {
			std::cerr << "error id code:" << code << std::endl;
			return false;
		}
		char tmp[8] = { 0 };
		memcpy(tmp, id.c_str() + 6, 4);
		year = atoi(tmp);
		memset(tmp, 0, sizeof(tmp));
		if (year < YEAR_MIN || year > YEAR_MAX) {
			std::cerr << "error id year:" << year << std::endl;
			return false;
		}
		memcpy(tmp, id.c_str() + 10, 2);
		month = atoi(tmp);
		memset(tmp, 0, sizeof(tmp));
		if (month < 1 || month > 12) {
			std::cerr << "error id month:" << month << std::endl;
			return false;
		}
		memcpy(tmp, id.c_str() + 12, 2);
		day = atoi(tmp);
		if (day < 1 || day > 31) {
			std::cerr << "error id day:" << day << std::endl;
			return false;
		}
		if (2 == month) {
			if ((0 == year % 4) && ((year % 100 != 0) || (0 == year % 400))) {
				if (day != 29) {
					std::cerr << "leap year:" << year << " error 2 month days:" << day << std::endl;
					return false;
				}
			}
			else {
				if (day != 28) {
					std::cerr << "non leap year:" << year << " error 2 month days:" << day << std::endl;
					return false;
				}
			}
		}
		return true;
	}
	std::string id;
	int code = 0;
	int year = 0;
	int month = 0;
	int day = 0;
};
class ids_process {
public:
	void add_id(const char *str) {
		if (!str) {
			return;
		}
		id_info info(str);
		if (true == info.parse_info()) {
			infos_.push_back(info);
		}
	}
	void sort_ids() {
		constexpr auto compare = [] (const id_info &info_one, const id_info &info_other) {
			if ((info_one.year == info_other.year) && (info_one.month == info_other.month) && (info_one.day == info_other.day)) {
				return info_one.id >= info_other.id;
			}
			else {
				if (info_one.year > info_other.year) {
					return true;
				}
				else if (info_one.year < info_other.year) {
					return false;
				}
				else if (info_one.month > info_other.month) {
					return true;
				}
				else if (info_one.month < info_other.month) {
					return false;
				}
				else return info_one.day >= info_other.day;
			}
		};
		std::sort(infos_.begin(), infos_.end(), compare);
	}
	void show_ids() const {
		std::cout << "=============show ids==============" << std::endl;
		for (auto &info : infos_) {
			std::cout << info.id << std::endl;
		}
	}
private:
	std::vector<id_info>infos_;
};
int main()
{
	int iCount = 0;
	const int MAX_LEN = 100;
	char id[MAX_LEN] = { 0 };
	printf("请输入身份证个数:");
	scanf("%d", &iCount);
	printf("\n");
	if (iCount > 100) {
		std::cerr << "invalid count:" << iCount << std::endl;
		return -1;
	}
	ids_process idsp;
	printf("请依次输入身份证:\n");
	getchar();
	for(int i=0; i<iCount; i++)
	{
		fgets(id, ID_LEN + 1, stdin); // add \0
		idsp.add_id(id);
		getchar();	// accept \n
	}
	idsp.sort_ids();
	idsp.show_ids();

	return 0;
}


3.编译

Makefile:

src = $(wildcard ./*.cpp) # 获取当前目录下所有.cpp文件
objs = $(patsubst %.cpp, %.o, $(src)) # 字符串替换,将.cpp替换为.o,%表示任意字符串
target = idsort
INCLUDE := -I.
LIBS    := -lpthread
CFLAGS  := -g -Wall  $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -std=c++11 -DHAVE_CONFIG_H
$(target): $(objs)
	$(CXX)  $^ $(LIBS) -o $@ 
# $(CXX) 代表g++
# $^ 表示所有的依赖文件
# $@ 表示要生产的目标


idsort.o: idsort.cpp
	$(CXX)  $(CXXFLAGS) -c $< -o $@

.PHONY:clean  # clean不是一个要生成的文件,假目标
clean:
	rm *.o -f

4.运行测试

 

<think>好的,我需要帮助用户找到PTA口罩发放问题的C++实现代码或解析。用户提到了引用[3]和[4],其中引用[4]明确提到了L2-034口罩发放PTA题目,而引用[3]中的代码片段是关于身份证验证和领取间隔判断的。用户需要的是C++代码或问题解析,所以我要结合这些信息来组织回答。 首先,我需要回忆PTA平台上的口罩发放题目要求。根据引用[4],题目大概涉及到处理市民信息,筛选符合条件的口罩发放名单。可能包括验证身份证合法性、检查提交时间间隔、排序输出等步骤。 接下来,用户提供的引用[3]中的代码片段有一个test函数,用于检查身份证是否18位且全是数字。但代码中的str.size()!=0似乎有误,应该是str.size()!=18。需要指出这个错误并给出正确的方法。 然后,引用[2]中的代码使用了map<vector<int>, int>来统计出现次数,并排序输出。这可能统计口罩申请记录有关,但需要根据具体题目调整结构。例如,处理市民信息时可能需要按提交时间排序,并检查时间间隔。 用户需要C++代码实现,所以需要分步骤解析: 1. 数据结构设计:市民信息的结构体,包含姓名、身份证、健康状况、提交时间等字段。 2. 身份证验证:确保身份证号18位且全为数字。 3. 时间处理:将提交时间转换为分钟数,方便计算间隔。 4. 合法性检查:包括身份证有效、健康状况为1、且在当天有效时间内提交。 5. 发放处理:按提交时间排序,并检查间隔是否满足条件。 6. 输出结果:先输出合法申请,再输出发放名单,注意同一人需间隔至少P天。 需要注意到引用[3]中的时间间隔判断,可能涉及将日期时间转换为统一的时间戳,比如分钟或秒,然后计算差值是否超过P天(如P*24*60分钟)。 在代码实现时,需要注意以下几点: - 输入处理:读取多条记录,解析时间和日期。 - 合法记录筛选:过滤掉无效身份证和不符合健康条件的记录。 - 排序:合法记录按提交时间升序排列。 - 发放名单生成:确保同一人每次领取间隔超过P天,这里可能需要一个map来记录每个人上次领取的时间。 - 输出格式:先输出合法申请数量,再按顺序输出发放名单。 另外,引用[2]中的排序方法可能适用,但需要根据题目具体要求调整比较函数。例如,按提交时间排序,可能需要对时间字符串进行处理,转换成可比较的数值。 可能遇到的问题包括时间格式的处理(如HH:MM和日期的结合),以及如何高效地检查同一人的时间间隔。例如,将日期和时间转换为自某个起点(如当天00:00)的总分钟数,可以方便计算时间差。 最后,代码示例需要包括这些步骤,并处理边界情况,比如身份证号不正确、时间格式无效等。同时,输出部分要正确格式化,确保姓名和身份证的对齐。</think>根据PTA L2-034口罩发放题目要求,以下为C++实现的关键解析和代码框架: ### 一、问题核心逻辑 1. **身份证校验**:需满足18位纯数字,如`(test(id) = id.length()==18 && 全为数字)` 2. **时间间隔计算**:需记录每人上次领取时间,判断间隔是否≥P天 3. **优先级排序**:合法申请按提交时间升序排列,同时间按输入顺序排列 ### 二、数据结构设计 ```cpp struct Citizen { string name, id; int health; // 健康状况(1为正常) string date_time; // 提交时间(格式"HH:MM MM/DD/YYYY") int order; // 输入顺序标记 int timestamp; // 转换为分钟数的时间戳 }; ``` ### 三、关键实现代码 ```cpp // 身份证校验函数 bool isValidID(const string& id) { if (id.length() != 18) return false; for (char c : id) if (!isdigit(c)) return false; return true; } // 时间转换函数(示例:将"15:30 03/25/2020"转为分钟数) int convertTime(const string& dt) { int hh, mm, month, day, year; sscanf(dt.c_str(), "%d:%d %d/%d/%d", &hh, &mm, &month, &day, &year); return ((day*24 + hh)*60 + mm); // 简化为当天时间戳 } // 主处理逻辑框架 vector<Citizen> processRequests(int P, int D) { map<string, int> last_issue; // 记录最后发放时间 vector<Citizen> valid, result; // 读取输入并过滤非法记录 while (n--) { Citizen c; cin >> c.name >> c.id >> c.health >> c.date_time; c.timestamp = convertTime(c.date_time); if (isValidID(c.id) && c.health == 1) { valid.push_back(c); } } // 按提交时间排序 sort(valid.begin(), valid.end(), [](const Citizen& a, const Citizen& b) { return a.timestamp != b.timestamp ? a.timestamp < b.timestamp : a.order < b.order; }); // 生成发放名单 for (auto& c : valid) { if (!last_issue.count(c.id) || (c.timestamp - last_issue[c.id]) >= P*1440) { // 1440=24h*60 result.push_back(c); last_issue[c.id] = c.timestamp; } } return result; } ``` ### 四、输出规范 1. 先输出合法申请数量 2. 再按顺序输出发放名单: ``` 张三 310101200001011234 李四 310101199912312345 ``` ### 五、注意事项 1. **时间精度处理**:实际题目需要处理多日期间隔,需完整解析年月日[^3] 2. **去重机制**:使用`map<string, int>`记录身份证最后发放时间 3. **边界条件**:首次申请不受间隔限制,同一人同时间只记录首次
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值