VisaFormExtractor.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using Aspose.Words;
  2. using Aspose.Words.Tables;
  3. /// <summary>
  4. /// 签证文件识别服务类
  5. /// </summary>
  6. public class VisaFormExtractor
  7. {
  8. public VisaApplicationData ExtractData(string filePath)
  9. {
  10. var data = new VisaApplicationData();
  11. data.FileName = System.IO.Path.GetFileName(filePath);
  12. // 加载文档
  13. Document doc = new Document(filePath);
  14. // 获取文档中的所有表格
  15. Table[] tables = doc.GetChildNodes(NodeType.Table, true).ToArray().Cast<Table>().ToArray();
  16. // 根据文件名或内容判断签证类型,并分发给不同的提取器
  17. // 这里是一个简单的判断逻辑
  18. if (data.FileName.Contains("澳新") || data.FileName.Contains("澳大利亚") || data.FileName.Contains("新西兰"))
  19. {
  20. data.VisaType = "ANZ";
  21. ExtractANZData(tables, data.ExtractedFields);
  22. }
  23. else if (data.FileName.Contains("美国"))
  24. {
  25. data.VisaType = "USA";
  26. ExtractUSAData(tables, data.ExtractedFields);
  27. }
  28. else if (data.FileName.Contains("申根"))
  29. {
  30. data.VisaType = "Schengen";
  31. ExtractSchengenData(doc, data.ExtractedFields); // 申根表可能不全是表格
  32. }
  33. // ... 添加其他签证类型的判断和提取方法
  34. return data;
  35. }
  36. /// <summary>
  37. /// 提取澳新签证表数据
  38. /// </summary>
  39. private void ExtractANZData(Table[] tables, Dictionary<string, string> fields)
  40. {
  41. // 遍历所有表格,寻找关键字段
  42. foreach (Table table in tables)
  43. {
  44. foreach (Row row in table.Rows)
  45. {
  46. for (int cellIndex = 0; cellIndex < row.Cells.Count; cellIndex++)
  47. {
  48. Cell cell = row.Cells[cellIndex];
  49. string cellText = cell.GetText().Trim().Replace("\a", ""); // 获取单元格文本并清理
  50. // 1. 识别“姓名”字段
  51. if (cellText.Contains("姓名") && !cellText.Contains("曾用名") && !cellText.Contains("拼音"))
  52. {
  53. // 假设“姓名”的值在同行下一个单元格
  54. string value = GetAdjacentCellValue(row, cellIndex, isNext: true);
  55. if (!string.IsNullOrEmpty(value))
  56. {
  57. fields["姓名"] = value;
  58. }
  59. }
  60. // 2. 识别“出生日期”字段
  61. if (cellText.Contains("出生日期"))
  62. {
  63. // 假设“出生日期”的值在同行下一个单元格
  64. string value = GetAdjacentCellValue(row, cellIndex, isNext: true);
  65. if (!string.IsNullOrEmpty(value))
  66. {
  67. // 使用正则表达式验证并清理日期格式
  68. var dateMatch = Regex.Match(value, @"\d{4}年\d{1,2}月\d{1,2}日");
  69. if (dateMatch.Success)
  70. {
  71. fields["出生日期"] = dateMatch.Value;
  72. }
  73. else
  74. {
  75. fields["出生日期"] = value; // 如果不是标准格式,也先存下来
  76. }
  77. }
  78. }
  79. // 3. 识别“性别”字段 (处理带有选项的情况)
  80. if (cellText.Contains("性 别") || cellText.Contains("性别"))
  81. {
  82. // 这个单元格里可能直接是“男”或“女”,也可能在后面的Run里有带格式的文本
  83. string value = GetTextFromCellOrFollowingRuns(cell);
  84. // 清理文本,只取“男”或“女”
  85. if (value.Contains("男")) fields["性别"] = "男";
  86. else if (value.Contains("女")) fields["性别"] = "女";
  87. }
  88. // 4. 识别“婚姻状况”字段 (处理打勾的情况)
  89. if (cellText.Contains("婚姻状况"))
  90. {
  91. // 方法1: 在单元格内查找被勾选的项 (例如 R已婚 中的 R 可能代表被选中)
  92. string innerText = cell.GetText();
  93. if (innerText.Contains("R已婚")) fields["婚姻状况"] = "已婚";
  94. else if (innerText.Contains("R未婚")) fields["婚姻状况"] = "未婚";
  95. // ... 其他选项
  96. // 方法2: 如果勾选框是符号,可能需要检查特定的Run的字体
  97. }
  98. // ... 根据您的“澳新表”模板,继续添加更多字段的提取逻辑
  99. // 例如:现单位名称、月收入、家庭成员、教育经历等。
  100. // 这些字段可能需要更复杂的逻辑,比如跨行、跨表格读取。
  101. }
  102. }
  103. }
  104. }
  105. /// <summary>
  106. /// 提取美国签证表数据 (逻辑类似,但映射规则不同)
  107. /// </summary>
  108. private void ExtractUSAData(Table[] tables, Dictionary<string, string> fields)
  109. {
  110. foreach (Table table in tables)
  111. {
  112. foreach (Row row in table.Rows)
  113. {
  114. for (int cellIndex = 0; cellIndex < row.Cells.Count; cellIndex++)
  115. {
  116. Cell cell = row.Cells[cellIndex];
  117. string cellText = cell.GetText().Trim().Replace("\a", "");
  118. // 美国表的“姓名”字段可能直接就是“姓名:”
  119. if (cellText.StartsWith("姓名:"))
  120. {
  121. // 值可能在同一个单元格的“:”后面,也可能在下一个单元格
  122. string value = cellText.Split(':').Last().Trim();
  123. if (string.IsNullOrEmpty(value))
  124. {
  125. value = GetAdjacentCellValue(row, cellIndex, isNext: true);
  126. }
  127. fields["姓名"] = value;
  128. }
  129. if (cellText.StartsWith("出生日期:"))
  130. {
  131. string value = cellText.Split(':').Last().Trim();
  132. if (string.IsNullOrEmpty(value))
  133. {
  134. value = GetAdjacentCellValue(row, cellIndex, isNext: true);
  135. }
  136. fields["出生日期"] = value;
  137. }
  138. // ... 添加美国表特有的字段,如“护照号码”、“DS160确认号”、“美国联系人”等。
  139. }
  140. }
  141. }
  142. }
  143. /// <summary>
  144. /// 提取申根表数据 (可能包含段落)
  145. /// </summary>
  146. private void ExtractSchengenData(Document doc, Dictionary<string, string> fields)
  147. {
  148. // 申根表可能不完全是表格,需要结合段落(Paragraph)来搜索
  149. NodeCollection paragraphs = doc.GetChildNodes(NodeType.Paragraph, true);
  150. foreach (Paragraph para in paragraphs)
  151. {
  152. string paraText = para.GetText().Trim();
  153. if (paraText.StartsWith("1.姓名:"))
  154. {
  155. // 提取姓名,可能在冒号后面,也可能在下一行或下一个Run
  156. fields["姓名"] = paraText.Replace("1.姓名:", "").Trim();
  157. }
  158. // ... 处理其他申根表字段
  159. }
  160. // 同时也检查表格
  161. Table[] tables = doc.GetChildNodes(NodeType.Table, true).ToArray().Cast<Table>().ToArray();
  162. // ... 类似ANZ和USA的方法,但使用申根表的映射规则
  163. }
  164. // --- 辅助方法 ---
  165. /// <summary>
  166. /// 获取同一行中相邻单元格的值
  167. /// </summary>
  168. /// <param name="row">当前行</param>
  169. /// <param name="currentCellIndex">当前单元格索引</param>
  170. /// <param name="isNext">true=下一个单元格,false=上一个单元格</param>
  171. /// <returns>相邻单元格的文本</returns>
  172. private string GetAdjacentCellValue(Row row, int currentCellIndex, bool isNext = true)
  173. {
  174. int targetIndex = isNext ? currentCellIndex + 1 : currentCellIndex - 1;
  175. if (targetIndex >= 0 && targetIndex < row.Cells.Count)
  176. {
  177. return row.Cells[targetIndex].GetText().Trim().Replace("\a", "");
  178. }
  179. return null;
  180. }
  181. /// <summary>
  182. /// 从一个单元格及其子Run中提取文本,有助于获取格式化的文本(如被勾选的项)
  183. /// </summary>
  184. private string GetTextFromCellOrFollowingRuns(Cell cell)
  185. {
  186. // 这是一个简化的版本,实际中可能需要递归遍历所有子节点
  187. return cell.GetText().Trim().Replace("\a", "");
  188. }
  189. // 这是一个通用模型,您需要为每种签证类型定义更详细的模型
  190. public class VisaApplicationData
  191. {
  192. public string FileName { get; set; }
  193. public string VisaType { get; set; } // 可根据文件名判断
  194. public Dictionary<string, string> ExtractedFields { get; set; } = new Dictionary<string, string>();
  195. }
  196. }