第一次个人编程作业

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 预估耗时(min) 实际耗时(min)
* 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里头了
  • 设计流程图:
    第一次个人编程作业_第1张图片

  • 处理的对象:

名称 说明 备注
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有点暴力 ……其余就不一一列出了,核心思想就是匹配查找+字符串切割、更新

四、性能改进

第一次个人编程作业_第2张图片
第一次个人编程作业_第3张图片
第一次个人编程作业_第4张图片
查找对比了一下突然发现主要耗时在转码和读入输出上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甚至想锤ymz,C++手造轮子 基本没队友+没人权=自闭……
  • 其实也是可以用正则匹配的,不过想了想反正都要打表匹配补全,还不如直接全暴力处理了主要函数都类似改起来也快0.0
    又碰上事情多的一周 当场裂开(满脸心酸.jpg 被自己菜哭)

你可能感兴趣的:(第一次个人编程作业)