address-parse.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /**
  2. * 需要解析的地址,type是解析的方式,默认是正则匹配
  3. * @param address
  4. * @param options:type: 0:正则,1:树查找, textFilter: 清洗的字段
  5. * @returns {{}|({area: Array, province: Array, phone: string, city: Array, name: string, detail: Array} & {area: (*|string), province: (*|string), city: (*|string), detail: (Array|boolean|string|string)})}
  6. * @constructor
  7. */
  8. const AddressParse = (address, options, provinceString, cityString, areaString) => {
  9. const { type = 0, textFilter = [] } = typeof options === 'object' ? options : {};
  10. if (!address) {
  11. return {}
  12. }
  13. const parseResult = {
  14. phone: '',
  15. province: [],
  16. city: [],
  17. area: [],
  18. detail: [],
  19. name: '',
  20. };
  21. address = cleanAddress(address, textFilter);
  22. // 识别手机号
  23. const resultPhone = filterPhone(address);
  24. address = resultPhone.address;
  25. parseResult.phone = resultPhone.phone;
  26. const resultCode = filterPostalCode(address);
  27. address = resultCode.address;
  28. parseResult.postalCode = resultCode.postalCode;
  29. // 地址分割
  30. const splitAddress = address.split(' ').filter(item => item).map(item => item.trim());
  31. // 找省市区和详细地址
  32. splitAddress.forEach((item) => {
  33. // 识别地址
  34. if (!parseResult.province[0] || !parseResult.city[0] || !parseResult.area[0]) {
  35. // 两个方法都可以解析,正则和树查找
  36. let parse;
  37. type === 1 && (parse = parseRegion(item, parseResult));
  38. type === 0 && (parse = parseRegionWithRegexp(item, parseResult, provinceString, cityString, areaString));
  39. const {province, city, area, detail} = parse;
  40. parseResult.province = province || [];
  41. parseResult.area = area || [];
  42. parseResult.city = city || [];
  43. parseResult.detail = parseResult.detail.concat(detail || []);
  44. } else {
  45. parseResult.detail.push(item);
  46. }
  47. });
  48. // 地址都解析完了,如果还没有姓名,那么姓名应该是在详细地址里面,取详细地址里面长度最小的那个
  49. if (!parseResult.name) {
  50. const detail = JSON.parse(JSON.stringify(parseResult.detail));
  51. detail.sort((a, b) => a.length - b.length);
  52. // console.log(judgeFragmentIsName(detail[0]));
  53. parseResult.name = detail[0];
  54. const nameIndex = parseResult.detail.findIndex(item => item === parseResult.name);
  55. parseResult.detail.splice(nameIndex, 1)
  56. }
  57. // log(JSON.stringify(parseResult));
  58. const province = parseResult.province[0];
  59. const city = parseResult.city[0];
  60. const area = parseResult.area[0];
  61. const detail = parseResult.detail;
  62. return Object.assign(parseResult, {
  63. province: (province && province.name) || '',
  64. city: (city && city.name) || '',
  65. area: (area && area.name) || '',
  66. detail: (detail && detail.length > 0 && detail.join('')) || ''
  67. })
  68. };
  69. /**
  70. * 利用正则表达式解析
  71. * @param fragment
  72. * @param hasParseResult
  73. * @returns {{area: (Array|*|string), province: (Array|*|string), city: (Array|*|string|string), detail: (*|Array)}}
  74. */
  75. const parseRegionWithRegexp = (fragment, hasParseResult, provinceString, cityString, areaString) => {
  76. // log('----- 当前使用正则匹配模式 -----')
  77. let province = hasParseResult.province || [], city = hasParseResult.city || [], area = hasParseResult.area || [],
  78. detail = [];
  79. let matchStr = '';
  80. if (province.length === 0) {
  81. for (let i = 1; i < fragment.length; i++) {
  82. const str = fragment.substring(0, i + 1);
  83. const regexProvince = new RegExp(`\{\"code\":\"[0-9]{1,6}\",\"name\":\"${str}[\u4E00-\u9FA5]*?\"}`, 'g');
  84. const matchProvince = provinceString.match(regexProvince);
  85. if (matchProvince) {
  86. const provinceObj = JSON.parse(matchProvince[0]);
  87. if (matchProvince.length === 1) {
  88. province = [];
  89. matchStr = str;
  90. province.push(provinceObj)
  91. }
  92. } else {
  93. break
  94. }
  95. }
  96. if (province[0]) {
  97. fragment = fragment.replace(matchStr, '')
  98. }
  99. }
  100. if (city.length === 0) {
  101. for (let i = 1; i < fragment.length; i++) {
  102. const str = fragment.substring(0, i + 1);
  103. const regexCity = new RegExp(`\{\"code\":\"[0-9]{1,6}\",\"name\":\"${str}[\u4E00-\u9FA5]*?\",\"provinceCode\":\"${province[0] ? `${province[0].code}` : '[0-9]{1,6}'}\"\}`, 'g');
  104. const matchCity = cityString.match(regexCity);
  105. if (matchCity) {
  106. const cityObj = JSON.parse(matchCity[0]);
  107. if (matchCity.length === 1) {
  108. city = [];
  109. matchStr = str;
  110. city.push(cityObj);
  111. }
  112. } else {
  113. break;
  114. }
  115. }
  116. if (city[0]) {
  117. const {provinceCode} = city[0];
  118. fragment = fragment.replace(matchStr, '');
  119. if (province.length === 0) {
  120. const regexProvince = new RegExp(`\{\"code\":\"${provinceCode}\",\"name\":\"[\u4E00-\u9FA5]+?\"}`, 'g');
  121. const matchProvince = provinceString.match(regexProvince);
  122. province.push(JSON.parse(matchProvince[0]))
  123. }
  124. }
  125. }
  126. if (area.length === 0) {
  127. for (let i = 1; i < fragment.length; i++) {
  128. const str = fragment.substring(0, i + 1);
  129. const regexArea = new RegExp(`\{\"code\":\"[0-9]{1,6}\",\"name\":\"${str}[\u4E00-\u9FA5]*?\",\"cityCode\":\"${city[0] ? city[0].code : '[0-9]{1,6}'}\",\"provinceCode\":\"${province[0] ? `${province[0].code}` : '[0-9]{1,6}'}\"\}`, 'g');
  130. const matchArea = areaString.match(regexArea);
  131. if (matchArea) {
  132. const areaObj = JSON.parse(matchArea[0]);
  133. if (matchArea.length === 1) {
  134. area = [];
  135. matchStr = str;
  136. area.push(areaObj);
  137. }
  138. } else {
  139. break
  140. }
  141. }
  142. if (area[0]) {
  143. const {provinceCode, cityCode} = area[0];
  144. fragment = fragment.replace(matchStr, '');
  145. if (province.length === 0) {
  146. const regexProvince = new RegExp(`\{\"code\":\"${provinceCode}\",\"name\":\"[\u4E00-\u9FA5]+?\"}`, 'g');
  147. const matchProvince = provinceString.match(regexProvince);
  148. province.push(JSON.parse(matchProvince[0]));
  149. }
  150. if (city.length === 0) {
  151. const regexCity = new RegExp(`\{\"code\":\"${cityCode}\",\"name\":\"[\u4E00-\u9FA5]+?\",\"provinceCode\":\"${provinceCode}\"\}`, 'g');
  152. const matchCity = cityString.match(regexCity);
  153. city.push(JSON.parse(matchCity[0]));
  154. }
  155. }
  156. }
  157. // 解析完省市区如果还存在地址,则默认为详细地址
  158. if (fragment.length > 0) {
  159. detail.push(fragment)
  160. }
  161. return {
  162. province,
  163. city,
  164. area,
  165. detail,
  166. }
  167. };
  168. /**
  169. * 利用树向下查找解析
  170. * @param fragment
  171. * @param hasParseResult
  172. * @returns {{area: Array, province: Array, city: Array, detail: Array}}
  173. */
  174. const parseRegion = (fragment, hasParseResult) => {
  175. // log('----- 当前使用树查找模式 -----')
  176. let province = [], city = [], area = [], detail = [];
  177. if (hasParseResult.province[0]) {
  178. province = hasParseResult.province
  179. } else {
  180. // 从省开始查找
  181. for (const tempProvince of provinces) {
  182. const {name} = tempProvince;
  183. let replaceName = '';
  184. for (let i = name.length; i > 1; i--) {
  185. const temp = name.substring(0, i);
  186. if (fragment.indexOf(temp) === 0) {
  187. replaceName = temp;
  188. break;
  189. }
  190. }
  191. if (replaceName) {
  192. province.push(tempProvince);
  193. fragment = fragment.replace(replaceName, '');
  194. break
  195. }
  196. }
  197. }
  198. if (hasParseResult.city[0]) {
  199. city = hasParseResult.city
  200. } else {
  201. // 从市区开始查找
  202. for (const tempCity of cities) {
  203. const {name, provinceCode} = tempCity;
  204. const currentProvince = province[0];
  205. // 有省
  206. if (currentProvince) {
  207. if (currentProvince.code === provinceCode) {
  208. let replaceName = '';
  209. for (let i = name.length; i > 1; i--) {
  210. const temp = name.substring(0, i);
  211. if (fragment.indexOf(temp) === 0) {
  212. replaceName = temp;
  213. break;
  214. }
  215. }
  216. if (replaceName) {
  217. city.push(tempCity);
  218. fragment = fragment.replace(replaceName, '');
  219. break;
  220. }
  221. }
  222. } else {
  223. // 没有省,市不可能重名
  224. for (let i = name.length; i > 1; i--) {
  225. const replaceName = name.substring(0, i);
  226. if (fragment.indexOf(replaceName) === 0) {
  227. city.push(tempCity);
  228. province.push(provinces.find(item => item.code === provinceCode));
  229. fragment = fragment.replace(replaceName, '');
  230. break;
  231. }
  232. }
  233. if (city.length > 0) {
  234. break;
  235. }
  236. }
  237. }
  238. }
  239. // 从区市县开始查找
  240. for (const tempArea of areas) {
  241. const {name, provinceCode, cityCode} = tempArea;
  242. const currentProvince = province[0];
  243. const currentCity = city[0];
  244. // 有省或者市
  245. if (currentProvince || currentCity) {
  246. if ((currentProvince && currentProvince.code === provinceCode)
  247. || (currentCity && currentCity.code) === cityCode) {
  248. let replaceName = '';
  249. for (let i = name.length; i > 1; i--) {
  250. const temp = name.substring(0, i);
  251. if (fragment.indexOf(temp) === 0) {
  252. replaceName = temp;
  253. break
  254. }
  255. }
  256. if (replaceName) {
  257. area.push(tempArea);
  258. !currentCity && city.push(cities.find(item => item.code === cityCode));
  259. !currentProvince && province.push(provinces.find(item => item.code === provinceCode));
  260. fragment = fragment.replace(replaceName, '');
  261. break;
  262. }
  263. }
  264. } else {
  265. // 没有省市,区县市有可能重名,这里暂时不处理,因为概率极低,可以根据添加市解决
  266. for (let i = name.length; i > 1; i--) {
  267. const replaceName = name.substring(0, i);
  268. if (fragment.indexOf(replaceName) === 0) {
  269. area.push(tempArea);
  270. city.push(cities.find(item => item.code === cityCode));
  271. province.push(provinces.find(item => item.code === provinceCode));
  272. fragment = fragment.replace(replaceName, '');
  273. break;
  274. }
  275. }
  276. if (area.length > 0) {
  277. break;
  278. }
  279. }
  280. }
  281. // 解析完省市区如果还存在地址,则默认为详细地址
  282. if (fragment.length > 0) {
  283. detail.push(fragment)
  284. }
  285. return {
  286. province,
  287. city,
  288. area,
  289. detail,
  290. }
  291. };
  292. /**
  293. * 匹配电话
  294. * @param address
  295. * @returns {{address: *, phone: string}}
  296. */
  297. const filterPhone = (address) => {
  298. let phone = '';
  299. // 整理电话格式
  300. address = address.replace(/(\d{3})-(\d{4})-(\d{4})/g, '$1$2$3');
  301. address = address.replace(/(\d{3}) (\d{4}) (\d{4})/g, '$1$2$3');
  302. address = address.replace(/(\d{4}) \d{4} \d{4}/g, '$1$2$3');
  303. address = address.replace(/(\d{4})/g, '$1');
  304. const mobileReg = /(\d{7,12})|(\d{3,4}-\d{6,8})|(86-[1][0-9]{10})|(86[1][0-9]{10})|([1][0-9]{10})/g;
  305. const mobile = mobileReg.exec(address);
  306. if (mobile) {
  307. phone = mobile[0];
  308. address = address.replace(mobile[0], ' ')
  309. }
  310. return {address, phone}
  311. };
  312. /**
  313. * 匹配邮编
  314. * @param address
  315. * @returns {{address: *, postalCode: string}}
  316. */
  317. const filterPostalCode = (address) => {
  318. let postalCode = '';
  319. const postalCodeReg = /\d{6}/g;
  320. const code = postalCodeReg.exec(address);
  321. if (code) {
  322. postalCode = code[0];
  323. address = address.replace(code[0], ' ');
  324. }
  325. return {address, postalCode}
  326. };
  327. /**
  328. * 地址清洗
  329. * @param address
  330. * @returns {*}
  331. */
  332. const cleanAddress = (address, textFilter = []) => {
  333. // 去换行等
  334. address = address
  335. .replace(/\r\n/g, ' ')
  336. .replace(/\n/g, ' ')
  337. .replace(/\t/g, ' ');
  338. // 自定义去除关键字,可自行添加
  339. const search = [
  340. '详细地址',
  341. '收货地址',
  342. '收件地址',
  343. '地址',
  344. '所在地区',
  345. '地区',
  346. '姓名',
  347. '收货人',
  348. '收件人',
  349. '联系人',
  350. '收',
  351. '邮编',
  352. '联系电话',
  353. '电话',
  354. '联系人手机号码',
  355. '手机号码',
  356. '手机号',
  357. ].concat(textFilter);
  358. search.forEach(str => {
  359. address = address.replace(new RegExp(str, 'g'), ' ')
  360. });
  361. const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\]\.<>/?~!@#¥……&*()——|{}【】‘;:”“’。,、?]", 'g');
  362. address = address.replace(pattern, ' ');
  363. // 多个空格replace为一个
  364. address = address.replace(/ {2,}/g, ' ');
  365. return address
  366. };
  367. export default AddressParse;