SAS - 导入不带分隔符的可变长度二进制记录

Posted

技术标签:

【中文标题】SAS - 导入不带分隔符的可变长度二进制记录【英文标题】:SAS - Importing variable length Binary records without delimiters 【发布时间】:2022-01-13 00:00:44 【问题描述】:

我有一个没有分隔符和固定长度记录的二进制数据集。我知道每条记录包含 22 字节的数据,然后是未知数量的 23 字节块,最多 50 个块。问题是它只读取 1 行 32767 字节,总共 728 obs。我期待 2.7MM 输出 obs。我怎样才能让它读到最后的输入文件?我已经尝试将“OBS=”选项和“lrecl=”选项添加到 infile 行。添加“end=”选项对结果没有影响。

DATA INFILE.MYDATA (drop= i);
INFILE "&Path./UGLYDATA" end=eof; 
INPUT
MY_KEY s370fPD9.
...
OCCURS s370fPD2.
@
;    
ARRAY   MyData50  MyData1-MyData50;
...
ARRAY   Filler50 $ Filler1-Filler50;

DO I = 1 TO min(50,OCCURS);
INPUT
MyDataI   s370fPD4.
...
FillerI   $ebcdic10.
@@
;
End;
RUN;

相关日志:

NOTE: 1 record was read from the infile "UGLYDATA".
      The minimum record length was 32767.
      The maximum record length was 32767.
      One or more lines were truncated.
NOTE: SAS went to a new line when INPUT statement reached past the end of a line.
NOTE: The data set INFILE.MYDATA has 728 observations and 356 variables.
NOTE: Compressing data set INFILE.MYDATA decreased size by 47.06 percent. 
      Compressed is 9 pages; un-compressed would require 17 pages.
NOTE: DATA statement used (Total process time):
      real time           2.69 seconds
      user cpu time       0.02 seconds
      system cpu time     0.11 seconds
      memory              1890.40k
      OS Memory           10408.00k
      Timestamp           12/07/2021 05:17:34 PM
      Step Count                        1  Switch Count  0
      Page Faults                       3
      Page Reclaims                     1028
      Page Swaps                        0
      Voluntary Context Switches        272
      Involuntary Context Switches      1226
      Block Input Operations            309648
      Block Output Operations           2312

【问题讨论】:

看起来您正试图在 Unix 机器上读取 IBM 大型机数据文件。你是怎么把文件送到 Unix 机器上的?它只是一个纯二进制字节流吗?它在大型机上是什么类型的文件? 整个文件有多少字节? OCCURS 是否有可能实际具有大于 50 的值?如果它确实是什么意思?只有前 50 个 OCCURS 值被写入文件? 【参考方案1】:

听起来文件不包含文本行。因此,请尝试在您的 INFILE 语句中使用 RECFM=N,这样 SAS 就不会寻找 LINEFEED 字符(或 CARRIAGE RETURN 和 LINEFEED 组合)来标记行尾。

INFILE "&Path./UGLYDATA" recfm=n ; 

如果您不确定文件包含什么,只需运行一个简单的数据步骤来查看前几百个字节,然后找出答案。如果“行”中的任何字节不是可打印字符,则 LIST 命令将在写入 SAS 日志时包含行下字节的十六进制代码。

data _null_;
  INFILE "&Path./UGLYDATA" recfm-=f lrecl=100 obs=10 ;
  input;
  list;
run;

【讨论】:

只需添加该选项即可使其完美运行。非常感谢!【参考方案2】:

根据@Tom,确实是RECFM=N

例子:

创建并读回一个二进制文件。

filename foo '%temp%/foo.bin' recfm=n;

data _null_;
  file foo;

  call streaminit(2021);

  filler = repeat('*', 10);

  do recnum = 1001 to 1010;
    put recnum s370fPD9. @;
    put filler $char11. @;

    occurs = rand('integer',1,26);
    put occurs s370fPD2. @; 

    do z = 0 to occurs-1;

      record = repeat(byte(rank('A')+z), 22);
      put record $ebcdic23.;

    end;

    putlog 'NOTE: ' recnum= occurs=;
  end;
  stop;
run;

data want;
  infile foo;

  * read master;
  input recnum s370fPD9. filler $char11. occurs s370fPD2.;

  * read details;
  do index = 1 to occurs;
    input content $ebcdic23.;
    output;
  end;
run;

dm 'vt want';

【讨论】:

以上是关于SAS - 导入不带分隔符的可变长度二进制记录的主要内容,如果未能解决你的问题,请参考以下文章

将 VB EBCDIC 文件转换为 ASCII,其中字帖记录以 01 分隔

跨多行拆分可变长度分隔字符串(SQL)

使用 Oracle SQL 将可变长度分隔字符串拆分为列

用于替换列表分隔符和字符串结尾的可变长度负向后搜索

使用流在 C++ 中读取可变长度输入

SAS宏从SAS数据集创建带分隔符的文本/csv文件。。