无需修改输入数据,如何使用SAS的CARDS语句创建目标表格?
无需修改原始输入的SAS CARDS数据集生成方案
问题背景
需要基于以下未对齐的原始输入数据,用SAS的CARDS语句生成数据集,但不想修改原始数据(当前可行方法是给输入数据添加空格对齐变量位置,才能通过列输入正确读取)。
原始输入数据
la grande vadrouille Louis de Funès 16 1966 A V 7 le mur de l’atlantique Jean Poiret 14 1970 AB 6 le cercle rouge Alain Delon 20 1970 Z A 8 le corniaud Louis de Funès 14 1965 SD 4 le chanteur de mexico Luis Mariano 12 1956 A B 5 la traversée de Paris Jean Gabin 19 1958 S 2
当前需修改输入的可行代码
DATA DMExo1.Q1; INPUT Film $CHAR22. Acteur $15. Note 39-40 Annee 41-45 Code $ 46-49 Ordre ; CARDS; la grande vadrouille Louis de Funès 16 1966 A V 7 le mur de l’atlantique Jean Poiret 14 1970 AB 6 le cercle rouge Alain Delon 20 1970 Z A 8 le corniaud Louis de Funès 14 1965 SD 4 le chanteur de mexico Luis Mariano 12 1956 A B 5 la traversée de Paris Jean Gabin 19 1958 S 2 ;RUN;
解决方案:正则表达式匹配提取
利用SAS的正则表达式功能,直接从原始未对齐的行中匹配提取各变量,无需修改输入数据。核心思路是通过正则表达式识别行内各部分的结构(电影名、演员名、数字字段、代码字段),精准提取对应内容。
实现代码
DATA DMExo1.Q1; length Film $22 Acteur $15 Note 8 Annee 4 Code $5 Ordre 8; infile cards truncover; input @; /* 读取整行但不移动输入指针 */ /* 编译正则表达式,匹配行内各字段结构 */ regex = prxparse('/^(.+?) (\w+(?: \w+)*) (\d+) (\d+) (.+) (\d+)$/'); if prxmatch(regex, _infile_) then do; Film = prxposn(regex, 1, _infile_); /* 提取电影名 */ Acteur = prxposn(regex, 2, _infile_); /* 提取演员名 */ Note = input(prxposn(regex, 3, _infile_), 8.); /* 提取评分,转为数值型 */ Annee = input(prxposn(regex, 4, _infile_), 4.); /* 提取年份,转为数值型 */ Code = prxposn(regex, 5, _infile_); /* 提取代码 */ Ordre = input(prxposn(regex, 6, _infile_), 8.); /* 提取序号,转为数值型 */ end; drop regex; /* 删除临时变量 */ CARDS; la grande vadrouille Louis de Funès 16 1966 A V 7 le mur de l’atlantique Jean Poiret 14 1970 AB 6 le cercle rouge Alain Delon 20 1970 Z A 8 le corniaud Louis de Funès 14 1965 SD 4 le chanteur de mexico Luis Mariano 12 1956 A B 5 la traversée de Paris Jean Gabin 19 1958 S 2 ; RUN;
代码说明
input @;:读取整行内容到_INFILE_变量中,且不移动输入指针,方便后续处理整行数据。- 正则表达式
/^(.+?) (\w+(?: \w+)*) (\d+) (\d+) (.+) (\d+)$/:^(.+?):非贪婪匹配电影名(匹配到演员名前的所有内容)(\w+(?: \w+)*):匹配演员名(支持带空格的姓名)(\d+):匹配评分(Note)和年份(Annee)的数字字段(.+):匹配代码字段(支持带空格的代码)(\d+)$:匹配最后一个数字序号(Ordre)
prxposn:从匹配结果中提取对应分组的内容,再通过input函数将数字类字段转为数值型。
替代方案:从后往前拆分字段
如果对正则不熟悉,也可以通过统计行内字段数量,从后往前拆分固定位置的数字字段,再处理前面的字符字段:
DATA DMExo1.Q1; length Film $22 Acteur $15 Note 8 Annee 4 Code $5 Ordre 8; array temp_fields[20] $30 _temporary_; /* 临时数组存储拆分后的所有字段 */ infile cards truncover; input @; field_count = countw(_infile_, ' '); /* 统计当前行的字段总数 */ /* 将整行拆分为单个字段存入临时数组 */ do i = 1 to field_count; temp_fields[i] = scan(_infile_, i, ' '); end; /* 提取末尾固定位置的数值字段 */ Ordre = input(temp_fields[field_count], 8.); Annee = input(temp_fields[field_count-2], 4.); Note = input(temp_fields[field_count-3], 8.); /* 根据字段总数提取代码字段 */ if field_count = 11 then Code = catx(' ', temp_fields[9], temp_fields[10]); else if field_count = 10 then Code = temp_fields[9]; else if field_count = 9 then Code = temp_fields[8]; /* 合并前面的字段为电影名+演员名,需手动拆分(已知分界的情况) */ Film = catx(' ', temp_fields[1], temp_fields[2], temp_fields[3]); Acteur = catx(' ', temp_fields[4], temp_fields[5], temp_fields[6]); /* 需根据每行电影名的字段数调整,示例仅适配第一行,通用场景建议用正则方案 */ drop field_count i temp_fields:; CARDS; la grande vadrouille Louis de Funès 16 1966 A V 7 le mur de l’atlantique Jean Poiret 14 1970 AB 6 le cercle rouge Alain Delon 20 1970 Z A 8 le corniaud Louis de Funès 14 1965 SD 4 le chanteur de mexico Luis Mariano 12 1956 A B 5 la traversée de Paris Jean Gabin 19 1958 S 2 ; RUN;
注意:此方案需要明确电影名和演员名的字段分界,否则拆分结果可能不准确,因此正则方案更通用。
内容的提问来源于stack exchange,提问作者goupi
相关产品推荐
相关产品推荐

