第一次个人编程作业

GitHub链接

https://github.com/huaku-i/031702147.git

一、前言

就是后悔,非常后悔,真的不懂为啥自己钢铁侠选了C++来写,可能作死吧
但是估计很多人都是用C++写的,想了想我好你好大家好,写个大家都看得懂的版本……QUQ
(这个C++版本只能实现非附加题的问题 看了百度API自动补全 觉得用PY比较好写,
这周末看看用PY 弄一个V2.0版本解决附加题应该会好点
PS:如果评测结果不对 那可能和评测工具不兼容吧

二、个人开发流程(PSP表格)

PSP2.1|Personal Software Process Stages| 预估耗时(分钟)|实际耗时(分钟)
:-😐:-😐:-😐:-😐:-:

  • Planning|计划|10 |25
    Estimate|估计这个任务需要多少时间|10|25
  • Development|开发|500|685
    Analysis|需求分析|180|120
    Design Spec|生成设计文档|15|20
    Design Review|设计复审|10|30
    Coding Standard|代码规范 |15|45
    Design|具体设计|20|20
    Coding|具体编码|200|240
    Code Review|代码复审|60|120
    Test|测试|60|90
  • Reporting|报告|60|80
    Test Repor|测试报告|30|30
    Size Measurement|计算工作量|10|10
    Postmortem & Process Improvement Plan|事后总结, 并提出过程改进计划|20|40
  • total|合计|570| 830

三、设计与实现

(1)分析与假设(通过询问,可能这就是面向数据编程我得到了一下几条有用的假设与信息

  • 输入输出都是 UTF8字符编码格式

  • 对于非附加题,第1级,省级行政区:在“XX省”这样的省后缀结尾可能省略“省”,不可能出现缺失

  • 对于非附加题,第2级,地级行政区:在“XX市这样的市后缀结尾”这样的可能省略“市”,同时第二级可能出现缺失

  • 对于非附加题,第3级-第7级地址都可能出现缺失,但不可能出现像前2级那样的省略了(也就是说,只可能省略“省”和“市”这两个字)

  • 假设输入一定是:X!姓名,+地址+电话给出,电话暂定为11位(其实别的位只要连续,也很容易判断)

  • Last but not least 如果我的询问和假设和最终样例出现了偏差…… 那我也没办法QUQ(估计评测当场裂开
    (2)需求与设计
    PS:因为不知道评测提交需要的要求和文件,怕出问题先写到一个CPP里头了

  • 设计流程图:

  • 处理的对象:

名称 说明 备注
id 地址id 直辖市1,自治区2,普通省份3
difficult 难度 分为1!、2!、3!
str 主字符串
name 名字 以逗号分隔
phone 电话 11位号码
address1 第1级地址 23个省、5个自治区、4个直辖市、2个特别行政区
address2 第2级地址 293个地级市、7个地区、30个自治州、3个盟
address3 第3级地址 963个市辖区、382个县级市、1329个县、117个自治县、49个旗、3个自治旗、1个林区、1个特区
address4 第4级地址 包括8393个街道、21297个镇、9120个乡、981个民族乡、152个苏木、1个民族苏木、1个县辖区
address5 第5级地址 路、巷、弄等
address6 第6级地址
address7 第7级地址 其他
other 附加
  • 核心函数 :Difficult + Solve +碎碎念

(1)UFT8处理 按顺序来,首先当然是那个UTF8的处理QUQ,这里首先要感谢东哥 一开始没考虑那么多 我用C++写到一半
忽然发现要处理中文字符非常麻烦,本来想直接转PY JAVA了后来东哥抬了我一手 让我知道怎么处理UTF8和Unicode转换

**点此查看详细代码 + **

//UTF8对应的字符串类型是wstring,其余操作和string一样
//一开始读进来的时候记得用string读入,然后转成UTF8 wstring 处理,最后再转回string输出
string UnicodeToUTF8(const wstring&s) {
	string ret;
	wstring_convert > wcv;
	ret = wcv.to_bytes(s);
	return ret;
}
wstring UTF8ToUnicode(const string &s) {
	wstring ret;
	wstring_convert > wcv;
	ret = wcv.from_bytes(s);
	return ret;
}
text.address1 = L"";//wstring定义时候要加一个L,其他同理

(2)其他string处理函数

**点此查看详细代码 + **

size_type find( const basic_string &str, size_type index );  //返回str在字符串中第一次出现的位置(从index开始查找),如果没找到则返回string::npos
string &insert(int p,const string &s);  //在p位置插入字符串s
string &replace(int p, int n,const char *s); //删除从p开始的n个字符,然后在p处插入串s
string &erase(int p, int n);  //删除p开始的n个字符,返回修改后的字符串
string substr(int pos = 0,int n = npos) const;  //返回pos开始的n个字符组成的字符串
void swap(string &s2);    //交换当前字符串与s2的值
string &append(const char *s);   //把字符串s连接到当前字符串结尾
void push_back(char c)   //当前字符串尾部加一个字符c
const char *data()const;   //返回一个非null终止的c字符数组,data():与c_str()类似,用于string转const char*其中它返回的数组是不以空字符终止,
const char *c_str()const;  //返回一个以null终止的c字符串,即c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同,用于string转const char*

(3)部分匹配函数代码
第1级:根据汉字匹配

**点此查看详细代码 + **

int s_address1() {//第一级处理,省级行政区,23个省、5个自治区、4个直辖市
	//直辖市1、自治区2、普通省份3(普通省份多特判一个黑龙江)
	//判断直辖市
	for (int i = 0; i < 4; i++) {
		wstring temp = L"";
		temp += text.str[0];
		temp += text.str[1];
		if (temp == special[i]) {
			text.address1 = text.str.substr(0, 2);
			text.str.erase(0, 2);
			if (text.str[0] == L'市')
				text.str.erase(0, 1);
			return 1;
		}
	}
	//判断自治区
	int k = text.str.find(L"自治区");
	if (k != text.str.npos) {
		text.address1 = text.str.substr(0, k + 2);
		text.str.erase(0, k + 2);
		return 2;
	}
	//判断普通省份
	if (text.str[0] == L'黑') {//特判黑龙江,因为在普通省份中特字数4个字
		if (text.str[3] != L'省') {
			text.str.insert(3, L"省");
		}
		text.address1 = text.str.substr(0, 4);
		text.str.erase(0, 4);
		return 3;
	}
	else {
		if (text.str[2] != L'省') {
			text.str.insert(2, L"省");
		}
		for (int i = 0; i <= 2; i++)
			text.address1 += text.str[i];
		text.str.erase(0, 3);
		return 3;
	}
	return 0;//第一级不可能缺失,除了附加题
}

第2级直接本地打表,暴力匹配+补全

**点此查看详细代码 + **

void s_address2() {
        //第二级处理,293个地级市、7个地区、30个自治州、3个盟,(有时候会出现缺失,因此需要匹配操作)
	//市、自治州
	if (text.id == 1) {//直辖市特判
		text.address2 = text.address1 + L"市";
		return;
	}
	else{//其他省份
		int k = text.str.find(L"盟");
		if (k != text.str.npos) {
			text.address2 = text.str.substr(0, k + 1);
			text.str.erase(0, k + 1);
			return;
		}
		k = text.str.find(L"自治州");
		if (k != text.str.npos) {
			text.address2 = text.str.substr(0, k + 3);
			text.str.erase(0, k + 3);
			return;
		}
		for (int i = 0; i <= 332; i++) {//地级市(可能会省略“市”,所以要补齐操作)//自治州、盟、地区、
			if (text.str.find(city[i]) ==0) {
				int k = city[i].length();
				text.str.insert(k, L"市");
				text.address2 = text.str.substr(0, k + 1);
				text.str.erase(0, k + 1);
				if (text.str[0] == L'市')
					text.str.erase(0, 1);
				return;
			}
		}
	}
	text.address2 = L"";//全部都没找到就为第二级全部缺失
	return;
}

第3级-第7级暴力识别匹配

(4)主解决函数

**点此查看详细代码 + **

void solve(string s) {
text.str = UTF8ToUnicode(s);//转码
if (text.str[0] == L'0')text.difficult = 0;
if (text.str[0] == L'1')text.difficult = 1;
if (text.str[0] == L'2')text.difficult = 2;
text.str.erase(0, 2);
int len = text.str.length();
text.str.erase(len - 1, 1);//处理前缀后缀英文句点,难度信息
s_phone();
s_name();
text.id = s_address1();
s_address2();
s_address3();
s_address4();
s_address5();
s_address6();
s_address7();

}

说实话V1.0有点暴力 ……其余就不一一列出了,核心思想就是匹配查找+字符串切割、更新

四、性能改进




查找对比了一下突然发现主要耗时在转码和读入输出上OTL ,当然匹配也花费了一些时间OTL
重新修改了一下 匹配用了KMP(虽然查找匹配量不大的话可能不太明显?)
同时优化了一下异常处理和输出JSON函数(GAN 最后C++还是得手敲轮子)QUQ

五、单元测试

构造了4个函数address_plus1()、address_plus2()、address_phone()、address_name()进行单元测试比对
同时也想了特殊EXAMPLE:


更过分的是……
凶残的不讲道理
询问福哥正确性+自己查了一下后
得出

  • 一级行政区省级行政区34个:23个省、5个自治区、4个直辖市、2个特别行政区;
  • 二级行政区地级行政区333个:293个地级市、7个地区、30个自治州、3个盟。
  • 三级行政区县级行政区2845个:963个市辖区、382个县级市、1329个县、117个自治县、49个旗、3个自治旗、1个林区、1个特区。(截止2019年9月)
  • 四级行政区乡级行政区39945个:包括8393个街道、21297个镇、9120个乡、981个民族乡、152个苏木、1个民族苏木、1个县辖区。(截止2018年底)
    同时构造了如下数据检测覆盖率,进行单元测试
    部分特殊input:
2!张三,湖北省随州随县吴山镇唐王街联宏村18883549874委会.
2!李四,福建省福州13756899511市鼓楼区鼓西街道湖滨路110号湖滨大厦一层.
1!王五,福建福州闽13599622362侯县上街镇福州大学10#111.
2!红太狼,福建省福州市鼓楼18960221533区五一北路123号福州鼓楼医院.
1!灰太狼,广东省东莞市凤岗13965231525镇凤平路13号.
2!喜羊羊,云南省湘西土家族苗族自治州13965231525香格里拉市福州路10#112.
1!小张,福建省福州市福清市龙江18150632336街道福山路43号中银公寓.
2!小明,福18150632336建福州福清福山路43号.
1!小洪,陕西省铜川18150632336福州路22号.
2!小王,陕西铜川18150632336.
2!小郑,福建.

相对output:

[{"地址":["湖北省","随州市","随县","吴山镇","唐王街","","联宏村委会"],"姓名":"张三","手机":"18883549874"},
{"地址":["福建省","福州市","鼓楼区","鼓西街道","湖滨路","110号","湖滨大厦一层"],"姓名":"李四","手机":"13756899511"},
{"地址":["福建省","福州市","闽侯县","上街镇","福州大学10#111"],"姓名":"王五","手机":"13599622362"},
{"地址":["福建省","福州市","鼓楼区","","五一北路","123号","福州鼓楼医院"],"姓名":"红太狼","手机":"18960221533"},
{"地址":["广东省","东莞市","","凤岗镇","凤平路13号"],"姓名":"灰太狼","手机":"13965231525"},
{"地址":["云南省","湘西土家族苗族自治州","香格里拉市","","福州路","","10#112"],"姓名":"喜羊羊","手机":"13965231525"},
{"地址":["福建省","福州市","福清市","龙江街道","福山路43号中银公寓"],"姓名":"小张","手机":"18150632336"},
{"地址":["福建省","福州市","","","福清福山路","43号",""],"姓名":"小明","手机":"18150632336"},
{"地址":["陕西省","铜川市","","","福州路22号"],"姓名":"小洪","手机":"18150632336"},
{"地址":["陕西省","铜川市","","","","",""],"姓名":"小王","手机":"18150632336"},
{"地址":["福建省","","","","","",""],"姓名":"小郑","手机":""}]

六、异常处理

  • 处理对输入为空时的警告输出
 if (text.str == L"")
            fout << "input error!" << endl;
  • 处理了输入不为UTF8时的警告输出
if (text.str[0] != L"1"||text.str[0] != L"2"||text.str[0] != L"3")
            fout << "string error!" << endl;
  • 同时也增加了对输入警告错误输出
        if (text.name == L"")
            fout << "name error!" << endl;
	if (text.phone == L"")
            fout << "phone error!" << endl;

对于匹配不到11位(暂定)的电话号码和识别不到姓名的异常情况会输出错误

七、ENDING(碎碎念)

  • 一开始是想到用PY写的……但是题目不让我用、
    开始写了之后吧……… 又改题目让用了
  • 输入输出格式一开始没那么多规定……写着写着,变UTF8了……(虽然说不改中文字符也过不去)本来想GG半路上华一大佬的py贼船了,又狠不下写了一半的代码
  • 问题解决了、、写的差不多了……才发现一些神奇的地区和设定问题,于是又自闭踏上了DEBUG和优化的漫漫路
  • 都做完了 才发现这评测工具无教程只有命令行 (甚至像病毒)才是最难的OTL,C++手造轮子 基本没队友+没人权=自闭……
  • 其实也是可以用正则匹配的,不过想了想反正都要打表匹配补全,还不如直接全暴力处理了主要函数都类似改起来也快0.0
    又碰上事情多的一周 当场裂开(满脸心酸.jpg 被自己菜哭)
posted @ 2019-09-17 16:40  繁星倒影  阅读(329)  评论(10)    收藏  举报