option.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. #!/usr/bin/env/python
  2. # -*- coding:utf-8 -*-
  3. import re, os
  4. import configs
  5. from utils.washutil import table_label_cleal
  6. import numpy as np
  7. from PIL import Image
  8. def option2block(option_con, item_no_type):
  9. """
  10. 选择题选项切分
  11. 对于选项切分部分,最好也像题号一样先自我切分纠错,但这样老师如果手误打错了字母,可能就解析出错!!!!!
  12. :return:
  13. """
  14. def del_table(ss):
  15. ss = re.sub(r"</?t[dr]>|</?tbody>|</?table>|</?div>|</?p>", "", ss.replace("<td><p>", " "))
  16. return ss
  17. # print('***********',option_con)
  18. if '<table><tbody><tr>' in option_con and \
  19. len(re.findall('<tr><td><p>(A\s*[..、、::].+?|\(A\)\s*[..、、]?.+?)</tr>', option_con.strip())) == 1:
  20. st_opt = re.search('<table><tbody><tr><td><p>(A\s*[..、、::].+?|\(A\)\s*[..、、]?.+?)</tr>',
  21. option_con.strip()).start()
  22. option_con = option_con.strip()[0:st_opt] + '\n' + del_table(option_con.strip()[st_opt:])
  23. # print("option_con:", option_con)
  24. option_con = re.sub(r"</table>\n*\s*(<p>)?\s*(A\s*[、、..::]|\(A\)\s*[、、..]?)(.+?)", r"</table>【【A、】】\3",
  25. option_con, flags=re.S)
  26. if re.search("\n\s*C", option_con) is None and re.search("\n\s*c", option_con):
  27. option_con = re.sub("\n\s*c", "\nC", option_con)
  28. # option_con = re.sub(r"(\n\s*(<img\s*src=\".*?\"\s(width|height|eq-code|data-latex|ocr-latex)=.*?[\"/]>\s*)+?\s*)(A[、、..::].+?)", r"\1\n\3", option_con.strip())
  29. option_con = re.sub(r"(\n\s*(<img\s*src=((?![/>]).)*?/>\s*)+?\s*)(A[、、..::].+?)", r"\1\n\4", option_con.strip())
  30. con = re.sub(r"\n\s*([A-H])\s*[、、..::](.+?)", r"\n【【\1、】】\2", option_con.strip()) # 行首的A、不能考虑,故得用strip
  31. if item_no_type == 1 and len(re.findall(r'【【[A-H]\s*[..、、]】】', con)) <= 2 and \
  32. len(re.findall(r'\([A-H]\)', con)) > 2: # 针对题干是第一种类型,选项是第二种类型的情况
  33. item_no_type = 2
  34. if item_no_type == 2:
  35. con = re.sub(r"\n\s*\(([A-Hc])\)\s*[、、..]?(.+?)", r"\n【【\1、】】\2", option_con)
  36. con = con.replace("</table>【【", "</table>\n【【")
  37. # print(11111,option_con)
  38. if item_no_type == 1:
  39. if len(re.findall(r'【【[A-H]\s*[..、、]】】', con)) <= 3:
  40. while re.search(r"\n\s*[A-H]\s*<img\s*src=.+?", con.replace(" ", "")): # 2020/7/15
  41. con = re.sub(r"\n\s*([A-H])\s*(<img\s*src=.+?)", "\n" + r"【【\1、】】\2", con)
  42. while re.search(r"(\n\s*<img\s*src=.+?)([A-H][..、、])(.+?)", con.replace(" ", "")):
  43. con = re.sub(r"(\n\s*<img\s*src=.+?)(?<!【)([A-H]\s*[..、、::])(.+?)", r"\1" + "\n" + r"【【\2】】\3", con)
  44. while re.search(r"(\n【【[A-H][..、、]】】.+?)(?<!【)([A-Hc][..、、])\n+(.+?)(?<!【)([A-H][..、、])(.+?)",
  45. con.replace(" ", ""), re.S):
  46. con = re.sub(r"(\n\s*【【[A-H]\s*[..、、]】】.+?)(?<!【)([A-H]\s*[..、、::])\s*\n+(.+?)"
  47. r"(?<!【)([A-H]\s*[..、、::])(.+?)", r"\1【【\2】】\3【【\4】】\5", con, flags=re.S)
  48. while re.search(r"(\n【【[A-H][..、、]】】.+?)(?<!【)([A-H][..、、])\n+(.+?)", con.replace(" ", ""), re.S):
  49. con = re.sub(r"(\n\s*【【[A-H]\s*[..、、]】】.+?)(?<!【)([A-H]\s*[..、、::])\s*\n+(.+?)",
  50. r"\1【【\2】】\3", con, flags=re.S)
  51. while re.search(r"(\n【【[A-H][..、、]】】.+?)(?<!【)([A-H][..、、])(.+?)", con.replace(" ", "")):
  52. con = re.sub(r"(\n\s*【【[A-H]\s*[..、、]】】.+?)(?<!【)([A-H]\s*[..、、::])(.+?)", r"\1【【\2】】\3", con)
  53. while re.search(r"(\n【【[A-H][..、、]】】[^【]+?/>\s+)(?<!【)([B-H][..、、])(.+?)", con.replace(" ", ""), re.S):
  54. con = re.sub(r"(\n\s*【【[A-H]\s*[..、、]】】[^【]+?/>\s+)(?<!【)([B-H]\s*[..、、::])\s*(.+?)",
  55. r"\1【【\2】】\3", con, flags=re.S) # 选项子母前面是图片 9/8
  56. if item_no_type == 2:
  57. if len(re.findall(r'【【[A-H][..、、]】】', con)) <= 3:
  58. while re.search(r"\n\s*\([A-H]\)\s*<imgsrc=.+?", con.replace(" ", "")): # 2020/7/15
  59. con = re.sub(r"\n\s*\(([A-H])\)\s*(<img src=.+?)", "\n" + r"【【\1、】】\2", con)
  60. while re.search(r"(\n\s*<imgsrc=.+?)(\([A-H]\)[..、、]?)(.+?)", con.replace(" ", "")):
  61. con = re.sub(r"(\n\s*<img src=.+?)\(([A-H])\)\s*[..、、]?(.+?)", r"\1" + "\n" + r"【【\2、】】\3", con)
  62. while re.search(r"(\n【【[A-H]、】】.+?)\(([A-H])\)[..、、]?\n+(.+?)\(([A-H])\)[..、、]?(.+?)",
  63. con.replace(" ", ""), re.S):
  64. con = re.sub(r"(\n\s*【【[A-H]、】】.+?)\(([A-H])\)\s*[..、、]?\s*\n+(.+?)"
  65. r"\(([A-H])\)\s*[..、、]?(.+?)", r"\1【【\2、】】\3【【\4、】】\5", con, flags=re.S)
  66. while re.search(r"(\n【【[A-H]、】】.+?)\(([A-H])\)[..、、]?\n+(.+?)", con.replace(" ", ""),re.S):
  67. con = re.sub(r"(\n\s*【【[A-H]、】】.+?)\(([A-H])\)\s*[..、、]?\s*\n+(.+?)",
  68. r"\1【【\2、】】\3", con, flags=re.S)
  69. while re.search(r"(\n【【[A-H]、】】.+?)\(([A-H])\)[..、、]?(.+?)", con.replace(" ", "")):
  70. con = re.sub(r"(\n\s*【【[A-H]、】】.+?)\(([A-H])\)\s*[..、、]?(.+?)", r"\1【【\2、】】\3", con)
  71. con_list = re.split(r"【【[A-H]\s*[..、、]】】", con)
  72. if len(con_list) > 1:
  73. stem_opt = table_label_cleal(con_list[0])
  74. con_list = list(map(del_table, con_list[1:]))
  75. con_list.insert(0, stem_opt) # 题干中的表格不需要清洗
  76. return con_list, con
  77. recur_n = 1 # 递归次数
  78. def option_structure(one_item, con, ans, item_no_type, is_danti=0, is_slave=0):
  79. """
  80. 选择题选项拆分结构化
  81. 还需要判断一下 选项个数与题型的对应!!!!
  82. :return:
  83. """
  84. global recur_n
  85. # print(con)
  86. # print('----------------------')
  87. if recur_n>2:
  88. if 'options' not in one_item and not is_slave:
  89. one_item["errmsgs"].append("选项格式不正确")
  90. recur_n = 1
  91. return one_item
  92. ans = re.sub("[;;.]+", "", ans)
  93. ans2 = []
  94. for a in ans.split("#"):
  95. if 0<len(a.replace(" ", "")) < 8:
  96. ans2.append("、".join(re.findall(r"[A-G]", a)))
  97. one_item["key"] = "; ".join(ans2)
  98. options_rank = get_options_arrange(one_item["stem"])
  99. # print("id:", one_item['item_id'])
  100. # print("options_rank:",options_rank)
  101. con_list, repl_con = option2block(con, item_no_type)
  102. # print(len(con_list), con_list)
  103. # 初筛
  104. if len(con_list) < 5:
  105. opt_letter = re.findall(r"【【([A-H])\s*[..、、]】】", repl_con)
  106. if opt_letter and opt_letter[0] == 'B' and re.search("<img src=.+?/>\s*A\s*[..、、].+?$", con_list[0]):
  107. re_split = re.sub("(<img src=.+?/>)\s*A\s*[..、、](.+?)$", r"\1【【A、】】\2", con_list[0])
  108. con_list[0] = re_split.split("【【A、】】")[0]
  109. con_list.insert(1, re_split.split("【【A、】】")[1])
  110. if len(con_list) >= 5:
  111. pattern_1 = re.compile(r"\s([1-9]|1[0-9])[..、、].+?([是为有]|等于)[((]\s*[))]\n", re.S)
  112. pattern_2 = re.compile(r"\s\(([1-9]|1[0-9])\).+?([是为有]|等于)[((]\s*[))]\n", re.S)
  113. pattern_3 = re.compile(r"([是为有]|等于)[((]\s*[))]\n", re.S)
  114. # 第一个错误针对题目中没有答案解析的情况,不然就是选项切分错误
  115. if not is_danti:
  116. if (item_no_type == 1 and any([True for op in con_list[1:] if re.search(pattern_1, op)])) or \
  117. (item_no_type == 2 and any([True for op in con_list[1:] if re.search(pattern_2, op)])):
  118. one_item["errmsgs"].append("本题选项与下一题题干间没有换行符,请注意重新换行!!!") # 一般只有一题和上一题连在一起
  119. if 'item_id' in one_item:
  120. one_item['spliterr_point'] = one_item['item_id']
  121. return one_item
  122. elif any([True for op in con_list[1:] if re.search(pattern_3, op)]):
  123. one_item["errmsgs"].append("本题的下一题的题号有问题,请注意重新输入!!!")
  124. if 'item_id' in one_item:
  125. one_item['spliterr_point'] = one_item['item_id']
  126. # ------------------------------------------------------------------------
  127. aft_opt = [] # 针对选项后是题目图片的情况
  128. if "\n" in con_list[-1]:
  129. ccon = re.split("\n+", con_list[-1])
  130. while re.match("<img src=", ccon[-1]) and len(ccon) > 1:
  131. aft_opt.insert(0, ccon[-1])
  132. ccon = ccon[:-1]
  133. if aft_opt:
  134. con_list[0] += "\n" + "\n".join(aft_opt)
  135. con_list[-1] = "\n".join(ccon)
  136. # -------------------------------------------------------------------------
  137. # 选项纠错
  138. con_list[0] = re.sub(r"\(\d+分\)", "", con_list[0][:9]) + con_list[0][9:]
  139. opt_letter = re.findall(r"【【([A-H])\s*[..、、]】】", repl_con)
  140. # print('/////////////////////////',opt_letter)
  141. if "".join(sorted(opt_letter)) in "ABCDEFGHIJ" or "".join(sorted(opt_letter)) in ["ABCE", "ABDE", "ACDE", "BCDE"]:
  142. # con_list = pic_transfer(con_list)
  143. if con_list:
  144. return dict(one_item, **{"stem": con_list[0],
  145. "options": [re.sub("(<br/>|\n)\s*$|\s+$", "", i) for i in con_list[1:]],
  146. "options_rank": options_rank,
  147. }) # , "options_num": len(con_list[1:])
  148. else:
  149. # 初次选项拆分的错误判断
  150. con_list = option_label_correct(opt_letter, con_list, repl_con)
  151. # double_l = [key for key, value in dict(Counter(opt_letter)).items() if value > 1]
  152. if type(con_list) == str:
  153. one_item["errmsgs"].append(con_list)
  154. return one_item
  155. else:
  156. # con_list = pic_transfer(con_list)
  157. if con_list:
  158. return dict(one_item,
  159. **{"stem": con_list[0],
  160. "options": [re.sub("(<br/>|\n)\s*$|\s+$", "", i) for i in con_list[1:]],
  161. "options_rank": options_rank,
  162. })
  163. # return dict(one_item, **dict(zip(["stem","A","B","C","D"], con_list)))
  164. else:
  165. # 选项可能放在表格中
  166. is_fail = 0
  167. con_list2 = re.split(r"\n+", con)
  168. errmsgs = ""
  169. if len(con_list2) == 2: # 选项是4个图片组成的情况
  170. option_array = len(re.findall("(^|\n)<img src=.+?", con_list2[1].strip()))
  171. if option_array > 2: # 排列情况
  172. options_rank = 1
  173. elif option_array > 1:
  174. options_rank = 3
  175. else:
  176. options_rank = 2
  177. ims = con_list2[1].split("<img src=")
  178. if len(ims) == 5 and re.search(r"[\u4e00-\u9fa5]", ims[0]) is None:
  179. con_list2 = [con_list2[0] if k == 0 else "<img src=" + v
  180. for k, v in enumerate(con_list2[1].split("<img src="))] # 默认将“<img src=”切分后的第一项丢掉了
  181. # if len(con_list2) == 5:
  182. con_list2[0] = re.sub(r"\(\d+分\)", "", con_list2[0].replace(" ", "")[:9]) + con_list2[0][9:]
  183. return dict(one_item, **{"stem": con_list2[0],
  184. "options": [re.sub("(<br/>|\n)\s*$|\s+$", "", i) for i in con_list2[1:]],
  185. "options_rank": options_rank,
  186. })
  187. else:
  188. errmsgs = """选项格式不正确,请改为: A.xxxx B.xxx 或 (A)xxxx (B)xxx,全文选项和题号格式要统一。
  189. 【注意】1>>选项和题干间要换行,选项不要放在表格中;2>>选项【如A.】重新手输;3>>选项图片时用嵌入式;
  190. 4>>选项太长时,每项之间要换行,上一项的内容不要与下一项在同一行!!"""
  191. is_fail = 1
  192. else:
  193. con_list3 = re.split(r"\n(?=<img)", con)
  194. if len(con_list3) == 5:
  195. return dict(one_item, **{"stem": con_list3[0],
  196. "options": [re.sub("(<br/>|\n)\s*$|\s+$", "", i) for i in con_list3[1:]],
  197. "options_rank": options_rank,
  198. })
  199. else:
  200. errmsgs = """选项格式不正确,请改为: A.xxxx B.xxx 或 (A)xxxx (B)xxx,全文选项和题号格式要统一。
  201. 【注意】1>>选项和题干间要换行,选项不要放在表格中;2>>选项【如A.】重新手输;3>>选项图片时用嵌入式;
  202. 4>>选项太长时,每项之间要换行,上一项的内容不要与下一项在同一行!!"""
  203. is_fail = 1
  204. op_con = re.split("[((]\s*[))]", con)[-1]
  205. stem_con = "".join(re.split("[((]\s*[))]", con)[:-1])+"( )\n"
  206. if is_fail:
  207. if "table" in op_con:
  208. to_clean_con = re.findall('<table>(((?!(</?table>)).)*)</table>', op_con, re.S)
  209. if len(to_clean_con) == 1:
  210. op_con = re.sub("</?table>|</?tr>|</?td>", "", op_con)
  211. one_item = option_structure(one_item, stem_con+op_con, ans, item_no_type)
  212. else:
  213. aa = re.findall("[A-E]", op_con)
  214. if len(aa)==len(set(aa)) == 4:
  215. recur_n += 1
  216. op_con = re.sub("(?<!\\\)([A-E])\s*(?![..、、])", r"\1、", op_con)
  217. one_item = option_structure(one_item, stem_con + op_con, ans, item_no_type, is_slave=is_slave)
  218. if not is_slave and 'options' not in one_item and "选项格式不正确" not in "".join(one_item["errmsgs"]):
  219. one_item["errmsgs"].append(errmsgs)
  220. return one_item
  221. def get_options_arrange(cont):
  222. """
  223. 判断word中选项每行排版个数
  224. :return:
  225. """
  226. options_rank = 1 # 纵向排列
  227. option_num = 0
  228. if '<table><tbody><tr>' in cont:
  229. table_op = re.findall('<tr>.+?>([A-H]\s*[..、、].+?|\([A-Z]\)\s*[..、、]?.+?)</tr>', cont.strip())
  230. if table_op:
  231. option_num = len(re.findall('[A-H]\s*[..、、].+?|\([A-Z]\)\s*[..、、]?.+?', table_op[0]))
  232. if option_num == 2:
  233. options_rank = 3
  234. if option_num > 2:
  235. options_rank = 2
  236. else:
  237. option_list = cont.split("\n")
  238. for op in option_list:
  239. if re.search("^\s*[A-H]\s*[..、、].+?|^\s*[A-H]\s*<img src=.+?|^\s*\([A-Z]\)\s*[..、、]?.+?", op.strip()):
  240. option_num += 1
  241. if option_num == 2:
  242. options_rank = 3 # 一排2个
  243. elif option_num < 2:
  244. options_rank = 2 # 横向排列
  245. return options_rank
  246. def new_options_rank(options):
  247. """
  248. 按提分宝产品B5纸176*250mm设置选项的排版形式
  249. 选项的排版形式暂设置3种:1:纵向排列 2:横向排列 3:一排2个
  250. 中文字符按5号字体,即10.5磅,英文字符按3/4个中文字符算
  251. :return:
  252. """
  253. options_rank = 1 # 纵向排列
  254. option_len = []
  255. for opt in options:
  256. if re.search("\$.*?\$", opt):
  257. return 0
  258. pic_len = []
  259. if "<img " in opt:
  260. for img in re.findall("<img src=.*?/>", opt):
  261. w_info1 = re.search(' style=".*?width: (\d+[.\d]*?)\s*([pxtin]*?);.*?"', img)
  262. w_info2 = re.search(' width="(\d+[.\d]*?)\s*([pxt]*?)"', img)
  263. if w_info1:
  264. if w_info1.group(2) == 'pt':
  265. pic_len.append((25.4/72)*float(w_info1.group(1)))
  266. elif w_info1.group(2) == 'px':
  267. pic_len.append((25.4 / 72) * (3/4) * float(w_info1.group(1)))
  268. elif w_info1.group(2) == 'in':
  269. pic_len.append(25.4 * float(w_info1.group(1)))
  270. elif w_info2:
  271. pic_len.append((25.4 / 72) * (3 / 4) * float(w_info2.group(1)))
  272. else:
  273. print("选项中存在图片宽高未知")
  274. # 主要没有宽高的图片是用户在编辑器新粘贴的图片,保存在本地,通过读取获取宽高
  275. w_info3 = re.search('<img src=.*?(/[^/]*?/new_image.*?)"', img)
  276. if w_info3:
  277. local_p = configs.IMG_FOLDER + w_info3.group(1)
  278. if os.path.exists(local_p):
  279. w = Image.open(local_p).size[0]
  280. pic_len.append((25.4 / 72) * (3 / 4) * float(w))
  281. else:
  282. print("选项中存在d的宽高未知图片不存在本地")
  283. options_rank = 0
  284. else:
  285. options_rank = 0
  286. opt = opt.replace(img, "")
  287. # 统计字符长度
  288. char_en_l = len(re.findall(r"[a-z\d,.!?;'\-/:<>=*+$~%()\[\]{}\" ]", opt))
  289. opt = re.sub(r"[a-z\d,.!?;'\-/:<>=*+$~%()\[\]{}\" ]", "", opt)
  290. char_zh_l = len(opt)
  291. char_len = (10.5/72)*25.4*(char_en_l*0.75+char_zh_l)
  292. option_len.append(sum(pic_len) + char_len)
  293. # 以最长的选项长度作为参考:<=6个中文字符则排成1行,<=15个中文字符则排成2排,否则都是纵向排列===>此逻辑不对
  294. if sum(option_len) + (len(options)*2 + (len(options)-1)*4)*(10.5/72)*25.4 < 176-40:
  295. options_rank = 2
  296. else:
  297. option_len = sorted(option_len, reverse=True)
  298. if option_len[0]+option_len[1] + (2*2+1*4)*(10.5/72)*25.4 <= 176-40:
  299. options_rank = 3
  300. return options_rank
  301. def option_label_correct(opt_letter, con_list, con):
  302. """
  303. 选项少切了会报错,所以优先解决多切的错误问题
  304. 纠正中标签错误的情况:选项字母不连续或重复;
  305. opt_letter:选项的字母 con_list:选择题拆分了选项的列表
  306. """
  307. lable_sign = re.findall(r"【【([A-H][..、、])】】", con.replace(" ", ""))
  308. con_list2 = con_list.copy()
  309. for i, j in enumerate(lable_sign): # 将con_list的选项字母加上
  310. con_list[i + 1] = j + con_list[i + 1]
  311. # con_list2 = re.split(r"【【[A-H]\s*[..、、]】】", con)
  312. p1 = 0 # 选项在con_list中的起始位置
  313. for k, v in enumerate(con_list[1:]):
  314. if re.search(r"[((]\s*[))]", v): # 选择题末尾一般都有()
  315. opt_letter[k] = '0'
  316. p1 = k + 2
  317. if p1 and p1 < len(con_list[1:]): # '0'不在最后一个位置
  318. option_list = con_list2[p1:]
  319. if len(option_list) >= 4:
  320. new_con_list = ["".join(con_list[:p1])].extend(option_list)
  321. return new_con_list
  322. else: # 只考虑ABCD和ACBD两种情况
  323. label_str = "".join(opt_letter)
  324. if re.match("A","".join(opt_letter)) is None:
  325. label_str = re.sub("[^A]A", "0A", "".join(opt_letter), count=1)
  326. # print(label_str)
  327. # -------------------------------------------------------------
  328. # 若选择题中没有(),题干中还是出现了AA的话,需要判断下是否存在错误
  329. if re.search("AA", label_str):
  330. label_bcd_idx = [k for k, i in enumerate(label_str) if i != 'A']
  331. label_a_idx = [k for k, i in enumerate(label_str) if i == 'A']
  332. length_all = []
  333. for i1 in label_bcd_idx: # 先将公式替换,作选项长度判断
  334. l1 = len(re.sub(r"<img\s*src\s*=\s*((?!/>).)+?/>", "<img>", con_list2[i1+1]).replace(" ",""))
  335. length_all.append(l1)
  336. aver_length = np.mean(length_all)
  337. st_a = label_str.index("AA")
  338. for i2 in label_a_idx:
  339. l2 = len(re.sub(r"<img\s*src\s*=\s*((?!/>).)+?/>", "<img>", con_list2[i2+1]).replace(" ",""))
  340. if abs(l2 - aver_length) >= 12:
  341. if i2 >= st_a:
  342. st_a = i2+1
  343. if st_a < len(label_str)-3:
  344. label_str = "".join(["0" if k < st_a else i for k, i in enumerate(label_str)])
  345. # -----------------------------------------------------------------
  346. label_str = re.sub("A[^BC]", "AA", label_str)
  347. label_str = re.sub("B[^CD]", "BB", label_str)
  348. label_str = re.sub("C[^BD]", "CC", label_str)
  349. label_str = re.sub("D[^E]", "DD", label_str)
  350. # 统计是否有重复的字符,若有,则进行合并,否则保持原来
  351. new_con_list = [con_list[0]]
  352. local_w = 0
  353. while local_w < len(label_str):
  354. if local_w == len(label_str) - 1 and label_str[local_w] == '0':
  355. break
  356. while label_str[local_w] == '0': # 如果‘0’在中间,则‘0’会被去除
  357. local_w += 1
  358. double_num = label_str.count(label_str[local_w])
  359. if double_num >= 2:
  360. new_con_list.append(con_list2[local_w + 1] + "".join(con_list[2 + local_w:local_w+double_num + 1]))
  361. else:
  362. new_con_list.append(con_list2[local_w + 1])
  363. local_w += double_num
  364. new_opt_letter = label_str.replace('AA',"A").replace('BB',"B").replace('CC',"C").replace('DD',"D")
  365. if len(new_con_list) >= 4:
  366. if "".join(sorted(new_opt_letter)) in "ABCDEFGHIJ" or "".join(sorted(new_opt_letter)) in ["ABCE", "ABDE", "ACDE", "BCDE"]:
  367. return new_con_list
  368. return "选项格式不正确,1、请改为: A.xxxx B.xxx,手动输入选项字母及后面的标点符号;" \
  369. "2.第一个选项A与题干之间要换行,各选项按ABCD排序;3.选项含图片时用嵌入式;"
  370. def table_option_struc(stem):
  371. """
  372. 表格类的选项结构化,在化学科目的选择题中较常见
  373. :return: 表格仍然作为表格,选项则根据表格中的选项补充,如A、A B、B
  374. """
  375. options = []
  376. may_options = re.findall("<table>(((?!(</?table>)).)*)</table>", stem)
  377. if may_options:
  378. options_data = may_options[-1][0]
  379. data_col = re.findall("<tr><td>(.*?)</td>", options_data) # 第一列
  380. if re.search("#?A#B#C#D#", re.sub("[..、、,,\s]", "", "#".join(data_col).strip())+"#"):
  381. options_str = re.sub("[..、、,,\s]", "", "#".join(data_col).strip()+"#")
  382. if "A#B#C#D#E#F#" not in options_str:
  383. if "A#B#C#D#E#" in options_str:
  384. options = ["A", "B", "C", "D", "E"]
  385. elif "A#B#C#D#" in options_str:
  386. options = ["A", "B", "C", "D"]
  387. else:
  388. data_rows = re.findall("<tr>(.*?)</tr>", options_data)
  389. data_row = re.findall("<td>(.*?)</td>", data_rows[0]) # 第一行
  390. if re.search("#?A#B#C#D#", re.sub("[..、、,,\s]", "", "#".join(data_row).strip()) + "#"):
  391. options_str = re.sub("[..、、,,\s]", "", "#".join(data_row).strip() + "#")
  392. if "A#B#C#D#E#F#" not in options_str:
  393. if "A#B#C#D#E#" in options_str:
  394. options = ["A", "B", "C", "D", "E"]
  395. elif "A#B#C#D#" in options_str:
  396. options = ["A", "B", "C", "D"]
  397. return options
  398. if __name__ == '__main__':
  399. stem ="""
  400. 下列物质与危险化学品标志的对应关系不正确的是<br/><table><tr><td>A</td><td>B</td><td>C</td><td>D</td></tr><tr><td>汽油</td><td>天然气</td><td>浓硫酸</td><td>氢氧化钠</td></tr><tr><td><img src="files/image2.png" width="125px" height="116px" /></td><td><img src="files/image3.png" width="117px" height="117px" /></td><td><img src="files/image4.png" width="118px" height="119px" /></td><td><img src="files/image5.png" width="122px" height="118px" /></td></tr></table>
  401. """
  402. # print(table_option_struc(stem))
  403. one_item = {
  404. 'errmsgs': [],
  405. 'key': 'C',
  406. 'parse': '【详解】根据题意可知,辐射出的光子能量$\\varepsilon = 3 . 5 2 \\times 1 0 ^ { - 1 9 } '
  407. 'J$,由光子的能量$\\varepsilon = h v$得<br/>$\\nu = \\frac { \\varepsilon } '
  408. '{ h } = 5 . 3 1 \\times 1 0 ^ { 1 4 } H z$<br/>故选C。',
  409. 'stem': '近年来,江西省科学家发明硅衬底氮化镓基系列发光二极管,开创了国际上第三条$L E D$技术路线。某氮化镓基$L E '
  410. 'D$材料的简化能级如图所示,若能级差为$2.20\\text{eV}$(约$3 . 5 2 \\times 1 0 ^ { - 1 9 '
  411. '} J$),普朗克常量$h = 6 . 6 3 \\times 1 0 ^ { - 3 4 } J \\cdot '
  412. 's$,则发光频率约为()<br/><img height="112px" src="/word/media/image5.png" '
  413. 'width="140px"/><br/>A.$6 . 3 8 \\times 1 0 ^ { 1 4 } H z$B.$5 . 6 7 '
  414. '\\times 1 0 ^ { 1 4 } H z$C.$5 . 3 1 \\times 1 0 ^ { 1 4 } H z$D.$4 '
  415. '. 6 7 \\times 1 0 ^ { 1 4 } H z$',
  416. 'topic_num': 1,
  417. 'type': '选择题',
  418. }
  419. one_item = option_structure(one_item, one_item["stem"], one_item["key"], 1)
  420. print(one_item)