Python对key为汉字的字典排序并生成汉字首字母目录

今天有想法去整理一个简单的题库,题目和答案来源于Fiddler抓手机包,目的是整理好题目、选项以及答案,并存到文件中以便答题时查找答案

全部操作流程是:

  1. 通过正则表达式以及json模块处理json字符串,得到一个key为题目,value为选项和答案的字典(去重);
  2. 对字典进行排序,根据字典key(题目)的第一个字符(多为汉字)的GBK编码进行升序排序
  3. 根据排序后的结果输出到文件中,并借助markdown实现汉字拼音首字母目录

整个程序的关键在于GBK编码,接下来按步记录过程。


1.处理字符串

抓包得到了3个json字符串数据存到txt里,然后通过正则和json进行处理,详情见代码:

def getQue_Ans():
    text = open(r'lgb_que_ans.txt', 'r', encoding="utf-8").read()
    text=re.sub(r'[ \t\n]','',text)
    text=re.sub(r'null','0',text)
    text=re.sub(r'false','0',text)
    data_lt = re.findall(r'"data":\[(.+?)\],"message":', text)
    print(len(data_lt))
    que_ans_dic={}
    for data in data_lt:
        #print(eval(data),file=f)
        tp=eval(data)
        for dic in tp:
            que = str(dic['quesSubject'])
            ans_opt = str(dic['quesOption'])
            ans = str(dic['quesAnswerC'])
            if que not in dic.keys():
                que_ans_dic[que]='=='+ans+'== \t\t'+ans_opt
    return que_ans_dic

2.对字典排序

对字典排序使用了sorted函数:sorted(可迭代对象,key=函数名,reverse=False/True)

那么,关键来了,怎样排序?

因为第一步得到的字典是以题目为key的,题目几乎都是汉字,那么很自然地想到按题目第一个汉字的拼音首字母来排序,就像汉语字典一样。如何实现这般排序,请先来了解一下GBK编码:

GBK编码:

  • 是对GB2312编码的扩展,采用双字节编码方案,其编码范围:8140-FEFE,剔除xx7F码位,共23940个码位。共收录汉字和图形符号21886个,其中汉字(包括部首和构件)21003个,图形符号883个。GBK编码支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。
  • 全部编码分为三部分:
    • 汉字区。包括:
      1. GB 2312 汉字区。即 GBK/2: B0A1-F7FE。收录 GB 2312 汉字 6763 个,按原顺序排列。
      2. GB 13000.1 扩充汉字区。包括:
        (1) GBK/3: 8140-A0FE。收录 GB 13000.1 中的 CJK 汉字 6080 个。
        (2) GBK/4: AA40-FEA0。收录 CJK 汉字和增补的汉字 8160 个。CJK 汉字在前,按 UCS 代码大小排列;增补的汉字(包括部首和构件)在后,按《康熙字典》的页码/字位排列。
        (3) 汉字“〇”安排在图形符号区GBK/5:A996。
    • 图形符号区。包括:
      1. GB 2312 非汉字符号区。即 GBK/1: A1A1-A9FE。其中除 GB 2312 的符号外,还有 10 个小写罗马数字和 GB 12345 增补的符号。计符号 717 个。
      2. GB 13000.1 扩充非汉字区。即 GBK/5: A840-A9A0。BIG-5 非汉字符号、结构符和“〇”排列在此区。计符号 166 个。
    • 用户自定义区:分为(1)(2)(3)三个小区。
      1. AAA1-AFFE,码位 564 个。
      2. F8A1-FEFE,码位 658 个。
      3. A140-A7A0,码位 672 个。
        第(3)区尽管对用户开放,但限制使用,因为不排除未来在此区域增补新字符的可能性。

其实这一串cv的内容我们只需要关注GB2312汉字区,也即GBK/2: B0A1-F7FE。为什么要关注这个编码段呢,其实大家去看一下GBK编码表就可以发现,首先这部分为常用汉字集,其次很显然地它以汉字的拼音首字母排序,方便我们后面目录的实现。

代码如下:

def getInitialLetter(gbk_code):
	#bytes eg: b'\xd2\xbb'  ('一')
    charGBK=int.from_bytes(gbk_code,byteorder='big',signed=False)
    if(charGBK >= 45217 and charGBK <= 45252):
        result = 'A'
    elif(charGBK >= 45253 and charGBK <= 45760):
        result = 'B'
    elif(charGBK >= 45761 and charGBK <= 46317):
        result = 'C'
    elif(charGBK >= 46318 and charGBK <= 46825):
        result = 'D'
    elif(charGBK >= 46826 and charGBK <= 47009):
        result = 'E'
    elif(charGBK >= 47010 and charGBK <= 47296):
        result = 'F'
    elif(charGBK >= 47297 and charGBK <= 47613):
        result = 'G'
    elif(charGBK >= 47614 and charGBK <= 48118):
        result = 'H'
    elif(charGBK >= 48119 and charGBK <= 49061):
        result = 'J'
    elif(charGBK >= 49062 and charGBK <= 49323):
        result = 'K'
    elif(charGBK >= 49324 and charGBK <= 49895):
        result = 'L'
    elif(charGBK >= 49896 and charGBK <= 50370):
        result = 'M'
    elif(charGBK >= 50371 and charGBK <= 50613):
        result = 'N'
    elif(charGBK >= 50614 and charGBK <= 50621):
        result = 'O'
    elif(charGBK >= 50622 and charGBK <= 50905):
        result = 'P'
    elif(charGBK >= 50906 and charGBK <= 51386):
        result = 'Q'
    elif(charGBK >= 51387 and charGBK <= 51445):
        result = 'R'
    elif(charGBK >= 51446 and charGBK <= 52217):
        result = 'S'
    elif(charGBK >= 52218 and charGBK <= 52697):
        result = 'T'
    elif(charGBK >= 52698 and charGBK <= 52979):
        result = 'W'
    elif(charGBK >= 52980 and charGBK <= 53688):
        result = 'X'
    elif(charGBK >= 53689 and charGBK <= 54480):
        result = 'Y'
    elif(charGBK >= 54481 and charGBK <= 55289):
        result = 'Z'
    else:
        result = 'other'
    return result

def sort2divide(que_ans_dic):
    # 分组 第一个字的拼音首字母
    #dic.item()是一个元组列表,[(key1,value1),(key2,value2),...]
    #匿名函数,传入key和value的二元组x,x[0]取到key,x[0][0]取到key的第一个字,转换为GBK编码
    que_ans_order=sorted(que_ans_dic.items(),key=lambda x:(x[0][0]).encode('GBK'),reverse=False)
    #返回一个列表对象[(key1,value1),(key2,value2),...]

    f = open(r"re_lgb_que_ans.md", 'w', encoding="utf-8")
    group=''
    index=1
    for item in que_ans_order:
        newgroup = getInitialLetter(item[0][0].encode('GBK'))#讲第一个字符的GBK编码传入getInitialLetter()函数
        if newgroup!=group:
            group=newgroup
            f.write('\n# '+group+'\n')
        #f.write(item[0]+'\n\t\t'+item[1]+'\n')
        print('%-3d'%index+' **'+item[0]+'**\n\t\t '+item[1]+'\n',file=f)
        index+=1
    f.flush()
    f.close()

ps:这一连串的if-elif归功于另外一篇博客,里面是用java实现的,参见文末链接
对字典进行排序其实只用到一行代码,其余是第三步的实现


3.输出

其实这一步的代码已经在上一步中实现了。

思路其实就是:

  • 按GBK编码升序排序后,其实已经符合拼音首字母顺序,那么我们在将字典中的每一条写到文件中去的时候,只需要判断字典的key的第一个字符的拼音首字母是否和上一个一致。如果不一致,则先输出首字母,再输出题目等
  • 因为bz这里的题库比较简单,去重之后只有100多个题,所以直接写到了md文件中,简单实现了目录以及格式的美化。

At Last:

if __name__=="__main__":
    sort2divide(getQue_Ans())

附结果部分图(请忽略内容🐶):


总结

利用GBK编码极大地简化了实现,但是同时也带来了限制。

  • 我们关注的字段只在45217-55289,也就是B0A1-D7F9,也就是说我们只包含了部分汉字、常用汉字。对于其他字段的数据我们只能把它归于other中。

  • 说是目录和美化,但其实只是利用了markdown的一些语法。但也是基于bz自己的数据较少,没有必要整成excel或者数据库等。

总之,虽然只是目的的简单实现,但也是比较有成就感的。

下一步打算用tkinter做成界面,通过输入框搜索get答案。


参见

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值