基于字符位置与长度提取RAW列结构化数据的技术咨询
数据解析需求:从RAW列提取指定字段到单独列
问题背景
我有一个包含RAW列的文件,数据示例如下:
STARTNA03N010110FIRST_NAME0211MIDDLE_NAME0309LAST_NAME0501S060829041987070110803PHL090101001M.....END
解析规则
- 从
START后提取姓名段,段标识符为NA03N01 - 字段格式为
XXYY:XX是字段标识符,YY是值的字符长度,后续对应长度的字符串为字段值01XX:名字(FNAME),必填02XX:中间名(MNAME),非必填03XX:姓氏(LNAME),必填04XX:母亲姓氏,非必填05XX:婚姻状况(CIVIL_STATUS),示例中0501S代表婚姻状况为S(单身)
- 姓名段后还有其他可变结构的段,如ID段(
ID03I01)、地址段(PA03A01)等,各段因非必填字段存在结构差异
现有尝试代码
我尝试基于段生成列,代码如下:
rows_2d = ["NA03N", "ID03I", "PA03A","EA03E"] all_codes = [] for code in rows_2d: for i in range(20): all_codes.append(code + ("{0:0=2d}".format(i+1)))
整数校验函数:
def isInt(s): try: int(s) return True except ValueError: return False
提取值的方法:
def get_next_characters_w_columns(code, string, columns): done = 0 output = "" code_index = string.find(code) if code_index == -1: return "None" a = code_index + len(code) while(done < len(columns)): if isInt(string[a:a+2]): fieldNo = int(string[a:a+2]) temp_output = string[a+4:a+4+int(string[a+2:a+4])] a = a + int(string[a+2:a+4]) + 4 if fieldNo in columns: output = output + temp_output done += 1 if done < len(columns): output = output + " " if fieldNo > max(columns): return output else: return output return output
调用方法:
def get_info(row, code, place=[i+1 for i in range(35)]): if "START" in row["RAW"]: info = get_next_characters_w_columns(code, row["RAW"], place) if (info == "-1") or (info == "-5"): return "No Info" else: return info else: return "None"
真实需求
直接从RAW列提取对应值并放入单独列(如FNAME、MNAME、LNAME等),而非先生成段再提取,避免非必填字段带来的额外处理难度。
解决方案
思路
直接定位姓名段起始位置,逐个解析字段,直到遇到其他段的标识符(如ID03I、PA03A等)为止,将每个字段值映射到对应的列,无需提前处理所有段结构。
实现代码
import pandas as pd # 定义字段标识符与目标列名的映射 FIELD_MAP = { "01": "FNAME", "02": "MNAME", "03": "LNAME", "04": "MOTHER_LNAME", "05": "CIVIL_STATUS" } # 定义其他段的起始前缀,用于终止姓名段解析 OTHER_SEGMENT_PREFIXES = {"ID03I", "PA03A", "EA03E"} def parse_raw_row(raw_str): # 初始化结果字典,所有字段默认值为None result = {col: None for col in FIELD_MAP.values()} # 定位START标记 start_idx = raw_str.find("START") if start_idx == -1: return result # 定位姓名段起始位置 name_segment_start = raw_str.find("NA03N01", start_idx) if name_segment_start == -1: return result current_pos = name_segment_start + len("NA03N01") str_length = len(raw_str) while current_pos + 4 <= str_length: # 检查是否进入其他段,是则停止解析 for prefix in OTHER_SEGMENT_PREFIXES: if raw_str.startswith(prefix, current_pos): return result field_id = raw_str[current_pos:current_pos+2] # 处理非目标字段:跳过该字段 if field_id not in FIELD_MAP: try: length = int(raw_str[current_pos+2:current_pos+4]) current_pos += 4 + length except ValueError: return result continue # 解析目标字段 try: length = int(raw_str[current_pos+2:current_pos+4]) field_value = raw_str[current_pos+4:current_pos+4+length] result[FIELD_MAP[field_id]] = field_value # 移动指针到下一个字段 current_pos += 4 + length except ValueError: # 解析失败则终止 return result return result # 示例使用:假设df是包含RAW列的DataFrame df = pd.DataFrame({ "RAW": ["STARTNA03N010110FIRST_NAME0211MIDDLE_NAME0309LAST_NAME0501S060829041987070110803PHL090101001MEND", "STARTNA03N010108JOHN_DOE0307SMITH0501MID03I01...END"] }) # 应用解析函数并合并到原DataFrame parsed_data = df["RAW"].apply(parse_raw_row).apply(pd.Series) df = pd.concat([df, parsed_data], axis=1) # 查看结果 print(df[["RAW", "FNAME", "MNAME", "LNAME", "CIVIL_STATUS"]])
代码说明
- FIELD_MAP:统一管理字段标识符和目标列名的对应关系,便于后续维护
- OTHER_SEGMENT_PREFIXES:标记其他段的起始前缀,确保姓名段解析不会越界
- parse_raw_row函数:
- 先定位
START和姓名段的起始位置,未找到则返回空结果 - 逐个解析字段:遇到其他段则停止,非目标字段直接跳过,目标字段解析值后存入结果字典
- 加入异常处理,避免因数据格式错误导致程序崩溃
- 先定位
- DataFrame应用:通过
apply将解析逻辑批量应用到每一行,再将结果展开为列合并到原表,直接生成FNAME、MNAME等单独列
该方案精准适配非必填字段的可变结构场景,无需先处理所有段,直接提取目标字段到对应列。
内容的提问来源于stack exchange,提问作者aldrean
相关产品推荐
相关产品推荐

