相关文章推荐
有情有义的牛排  ·  望京SOHO“煞气”太重? ...·  1 年前    · 
干练的围巾  ·  环球人物·  1 年前    · 
健壮的火腿肠  ·  焦点访谈:目录上“新”强保障_新闻频道_央视 ...·  1 年前    · 
喝醉的板栗  ·  暗部资源共享群 - 百度·  1 年前    · 
逼格高的木瓜  ·  拯救者Y9000P2022版安装Ubuntu ...·  1 年前    · 
Code  ›  SAS 程序分享:使用 SAS 合并 RTF 文档 -
文件头 rtf sas rtf文件
https://www.bilibili.com/read/cv28836419/
正直的松球
5 月前
打开 App

SAS 程序分享:使用 SAS 合并 RTF 文档

  • 学习 ·
  • 2023-12-28 04:19 ·
  • 312浏览
SAS骆豪
关注

从事 SAS 临床试验程序员工作,常常会根据 SAP ( 统计分析计划 ) 产生大量的 TFL ( 表格、图形和列表 ) 文件,而它们都是 SAS 输出的 RTF 格式的文档,里面存储着 SAS 生成的统计分析报表。一般来说,一个项目可能会有 100~300 份 RTF 结果文档,显然,逐个打开、浏览、修订它们是费时费力的,通常的做法是将多份 RTF 结果文档合并成一份,然后直接使用合并后的文档。那么,要如何将多份 RTF 文档合并成一份呢?

直接的思路是使用 Microsoft Word 软件的「插入文件」功能,它将允许使用者在光标处一次性插入多份文档,然后将当前文档另存为新的文档即可。

图1 Word 中的插入文件功能

该功能无需任何辅助工具,操作简便,速度较快,但也存在一些缺点:不好定制插入文件的顺序,不好在相邻文件之间插入换行、分页、分节等特殊操作。这些缺点可以通过编写 VBA 程序克服,而笔者给出的做法,是通过 SAS 程序来完成 RTF 文档的合并,并通过特定的步骤、宏变量来增强合并功能。

使用 SAS 程序合并 RTF 文档,共有五个步骤:

  1. 获取 RTF 文件列表;

  2. 按照文件编号对列表进行排序;

  3. 按列表逐个读取 RTF 文件,并将它们的内容输出到数据集;

  4. 对内容数据集进行处理;

  5. 将内容数据集输出为单份 RTF 文件;

在执行这些业务之前,首先定义三个宏变量以供使用:

  • path : RTF 文件所在路径。

  • outfile : 合并后的文件名 ( 含路径 ) 。

  • split : 相邻文件的间隔符,可选: line 、 page 、 sect 、 none( 注意需要写成小写,因后面插入到 RTF 中要求小写 ) ,分别表示换行符、分页符、分节符、空。

1. 获取 RTF 文件列表

要将指定路径下的文件列表写入数据集,可以借助带管道操作符的终端命令语句 : X filename pipe statement ,或者使用文件 I/O 类函数。考虑到一些 SAS 客户端禁用了终端命令,笔者使用的是后者:

注意,若没有文件的读取权限,则后续的程序将运行失败,因此笔者加入了 fopen() 与 fclose() 操作,以确保文件可被读取。

2. 对文件列表排序

接下来,需要对这份文件列表进行排序,以保证后续输出内容时,按照预想的顺序执行。排序是基于文件名本身的,文件格式后缀不包括在内。

在命名一个多层级图表文档时,通常的做法是使用下划线、横杠或点号连接不同层级中的序号,例如存储表 4-2 的文档命名为 "Tab4_2.rtf" ,存储图 3.1.4 的文档命名为 "Fig3.1.4.rtf" 。有时,图表制作人员希望图 2-1 紧随在表 2-3 的后面,还会将存储图形的文档命名为 "Tab2_3_Fig2_1.rtf" 。

因此,对文件名排序首先要将名称中的各层级的编号提取出来,然后再按照层级依次升序排序。

上述程序使用了一个长度为 10 的字符数组,最多可容纳 10 个层级的文件名称,对于日常使用已经足够。而在使用 proc sort 进行排序时,将选项 sortseq= 指定为 linguistic(numeric_collation=on) 将允许对存储在字符型变量中的数值进行排序,若不使用这种做法, proc sort 完全按照字符的编码顺序对字符型变量进行排序,会得到 "Tab11" 排在 "Tab2" 前面这种不期望的结果。

3. 按列表将 RTF 文件读入数据集

RTF 文件本质上是一种文本文件,由无格式文本、控制字、控制符和群组构成,可以使用文本查看器看到其中的内容。而使用专业的文字处理软件打开——例如 Microsoft Word ——看到的则是排版整齐、布局漂亮的图文,这些格式控制,便是由 RTF 中的控制字和控制符定义的。

图2 示例 RTF 文档内容
图3 示例 RTF 文档在 MS WORD 中的显示内容

在拥有文本文件名称列表的情况下,要将其内容读入 SAS,可以继续使用文件I/O类函数,也可以使用带 filevar= 选项的 infile 语句 ¹ 。

令 filevar= 选项指向存储文件全路径名称的数据集变量,再配合 input 语句,可依次将文件列表中全部文件的内容依次读入并存储到 SAS 数据集的变量 content 中。这里用到了一个复合循环,通常的复合循环有两个终止条件,但此处只有一个 : "eof 为真 " ,这让 contnum 成为了一个记录文件内容行数的自增变量。即 contnum 从 1 开始自增,每循环一次就增加 1 ,直到读取到当前文件的最后一行内容时,自增结束,读取下个文件时 contnum 又从 1 开始自增,这等价于:

4. 处理 RTF 内容

按余阳 ² 等人的研究,RTF 可分为文件头和文档区,信息群组<info>是文档区的开头,它存储了文档标题、作者、创建时间等信息。

按Zhiping Yan ³ 等人的研究,合并多份 RTF 文件时,应仅保留第一个文件的文件头,最后一个文件的文档区群组闭合右括号,以及所有文件的文档区其它内容。合并规则的示意图如下:

图4 RTF 合并规则示意图

因此,只需要找到每个 RTF 文档内容中的 <info> 群组,以标记文件头和文档区,再按照合并规则删去部分内容就可以了。

上述代码中,有一条 if 0 then set … 语句,乍看上去有些奇怪,条件为假,后面的命令便不会执行,但数据步的运行包含「编译」和「运行」两个部分,当数据步包含 set 语句时,会在「编译」阶段获取 nobs= 、 end= 等数据集选项的值,并存入 PDV 中,而进入「运行」阶段后, if 语句才开始执行,此时尽管条件为假, nobs= 选项的值却早就被获取到了。

另外,在 RTF 中要表示换行、分页、分节,应当使用控制字 ⁴ : \line、\page、\sect ,这就是程序中trim(content)||"\&split."的含义,即在变量 content 的尾部连接控制字。而当控制字写成 \none 时,这不是合法的 RTF 控制字,RTF 阅读器会自动将其忽略。

5. 输出 RTF 内容

要将数据集中的 RTF 内容重新输出到 RTF 文档,只需要在数据步中使用 file 语句指定输出文件,配合 put 语句就可以将指定变量内容进行输出。

一个值得注意的地方是对输入数据集使用的 keep= 选项,由于工作中生成的 RTF 文件数量很多,到当前步骤时,内容数据集的观测数往往高达数十万条,此时限制读入 PDV 的变量数就能够避免不必要的 I/O 操作,从而加快运行速度。

参考文献

[1] 巫银良 . SAS 技术内幕 [M]. 1. 清华大学出版社 , 2018 :163-164.

[2] 余阳 . RTF 格式文件分析及在多媒体中的应用 [J]. 计算机工程 , 1999, 25(4).

[3] Zhiping Yan. A Fully Automated Approach to Concatenate RTF outputs and Create TOC[EB/OL]. 2015[2023-12-26]. https://lexjansen.com/pharmasug-cn/2015/DV/PharmaSUG-China-2015-DV28.pdf.

[4]Biblioscape 9. Rich Text Format (RTF) Version 1.5 Specification[EB/OL]. [2023-12-26]. https://www.biblioscape.com/rtf15_spec.htm.

本文禁止转载或摘编

本文为我原创

  • SAS
  • --
  • --
  • --
展开阅读全文
打开App,看更多精彩内容
 
推荐文章
有情有义的牛排  ·  望京SOHO“煞气”太重? “神棍局”公号为何被判赔20万?_网易订阅
1 年前
干练的围巾  ·  环球人物
1 年前
健壮的火腿肠  ·  焦点访谈:目录上“新”强保障_新闻频道_央视网(cctv.com)
1 年前
喝醉的板栗  ·  暗部资源共享群 - 百度
1 年前
逼格高的木瓜  ·  拯救者Y9000P2022版安装Ubuntu_拯救者安装ubuntu_我是小猴子啦啦啦的博客-CSDN博客
1 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号