基于包含元数据的文件构建SAS数据集

基于包含元数据的文件构建SAS数据集,sas,metadata,sas-macro,Sas,Metadata,Sas Macro,我有两个文本文件,一个包含没有标题的原始数据,另一个包含相关的列名和长度。我想使用这两个文件构建一个SAS数据集,其中包含来自一个文件的数据,以及来自另一个文件的列名和长度 包含数据的文件是固定宽度的文本文件。也就是说,每列数据都与文本文件的特定列对齐,并用空格填充以确保对齐 datafile.txt: John 45 Has two kids Marge 37 Likes books Sally 29 Is an astronaut Bill 60 Drinks c

我有两个文本文件,一个包含没有标题的原始数据,另一个包含相关的列名和长度。我想使用这两个文件构建一个SAS数据集,其中包含来自一个文件的数据,以及来自另一个文件的列名和长度

包含数据的文件是固定宽度的文本文件。也就是说,每列数据都与文本文件的特定列对齐,并用空格填充以确保对齐

datafile.txt:

John   45   Has two kids
Marge  37   Likes books
Sally  29   Is an astronaut
Bill   60   Drinks coffee
包含元数据的文件由两列以制表符分隔:一列为数据文件中列的名称,另一列为该列的字符长度。这些名称按它们在数据文件中的显示顺序列出

metadata.txt:

Name  7
Age  5
Comments  15
我的目标是创建一个如下所示的SAS数据集:

Name   | Age  | Comments
-------+------+-----------------
John   | 45   | Has two kids
Marge  | 37   | Likes books
Sally  | 29   | Is an astronaut
Bill   | 60   | Drinks coffee
我希望每个列都是元数据文件中指定长度的字符

必须有比我的天真方法更好的方法,那就是使用导入的元数据构造
length
语句和
input
语句,如下所示:

/*导入元数据*/
元数据;
长度colname$50科伦8;
infle'C:\metadata.txt'dsd dlm='09'x;
输入colname$collen;
跑
/*构造长度和输入语句*/
数据为空;
长度透镜输入1000美元;
保留lenstmt inptstmt“”colstart 1;
设置meta end=eof;
调用catx(“”,lenstmt,colname,“$”,colen);
调用catx(“”,inptstmt,cats(“@”,colstart),colname,“$&”);
colstart+collen;
如果是eof,那么做;
调用symputx('lenstmt',lenstmt);
调用symputx(“inptstmt”,inptstmt);
结束;
跑
/*导入数据文件*/
数据文件;
长度&透镜;
填充'C:\datafile.txt'dsd dlm='09'x;
输入&inptstmt;
跑
这让我得到了我需要的,但必须有一个更干净的方法。如果为存储
length
input
语句的变量分配的空间不足,或者如果语句长度超过了最大宏变量长度,这种方法可能会遇到麻烦


有什么想法吗?

请打电话给我,我可以帮忙

data _null_;
retain start 0;
infile 'c:\metadata.txt' missover end=eof;
    if _n_=1 then do; 
        start=1;
        call execute('data final_output; infile "c:\datafile.txt" truncover; input ');
    end;

input colname :$8.
      collen  :8.
      ;

call execute( '@'|| put(start,8. -l) || ' ' || colname || ' $'|| put(collen,8. -r) ||'. ' );
start=sum(start,collen);

    if eof then do;
        call execute(';run;');
    end;
run;
proc contents data=final_output;run;

你所做的是一种相当标准的方法。是的,你可以更仔细地检查;例如,为了谨慎起见,我会为这两条语句分配32767美元

不过,有一些方法可以改善这一点,这可能会消除你的一些担忧

首先,一个常见的解决方案是在行级别构建它(正如您所做的),然后使用
proc sql
创建宏变量。这比数据步方法有更大的最大长度限制(数据步方法的最大长度是32767美元,如果不使用多个变量,SQL是64kib的两倍)

其次,您可以通过写入文件而不是宏变量来超过64k限制。在数据步骤中,不要累加然后使用
call symput
,而是将每一行写入一个
temp
文件(或两个)。然后,<>代码> %>代码>那些文件,而不是在输入数据步中使用宏变量——是的,你可以<代码> %包含在数据步中间的。< /P>
还有其他方法,但这两种方法是最常见的,应该适用于大多数用例。其他一些方法包括
callexecute
run\u macro
,或使用文件打开命令直接处理文件。一般来说,这两种方法要么比最常见的两种方法更复杂,要么没有那么有用,尽管它们当然也是可以接受的解决方案,在实践中并不少见。

为什么这比OP的例子更干净?如果你认为这是一种改进,请在答案中解释原因,并提供更多信息。@Joe-我认为问题的最后部分回答了为什么我的解决方案更干净。实现此解决方案的用户不需要担心任何大小限制。此外,不会浪费任何数据步骤,也不会创建任何浪费的宏变量。我不反对宏,但我喜欢在需要时使用它们。使用基于调用执行的解决方案,用户无需担心datastep变量大小或宏变量大小限制。然后将其放入您的答案中。你的答案基本上就是代码。仅仅是代码的答案不是很好的答案-他们应该解释它们是如何工作的(至少在某种程度上)以及为什么它们应该为手头的问题工作。是否有一种方法也可以使用
proc sql
完成运行求和,从而在
input
语句中获取输入列?或者,这应该在数据步骤中提前完成?这是您希望提前完成的。对于这种特殊情况,我可能会使用
%include
方法。
proc sql;
  select catx(' ',colname,'$',collen)
    into :lenstmt separated by ' '
    from meta; *and similar for inputstmt;
quit;