文件、流和外部运算
读取和写入 Wolfram 系统文件 | 搜索文件 |
外部程序 | 搜索并读取字符串 |
流与低级输入和输出 | 二进制文件 |
命名及查找文件 | 生成 C 和 Fortran 表达式 |
处理文件和目录 | Wolfram 语言脚本 |
读入文本数据 |
在外部文件中存储 Wolfram 语言表达式
可以使用计算机系统上的文件来存储 Wolfram 语言的定义和结果. 最通用的方法是将所有内容存储为适合 Wolfram 语言输入的纯文本格式. 通过这种方法,在一个计算机系统上运行的 Wolfram 语言版本可生成在任何计算机系统上运行的版本都可以读取的文件. 此外,其他标准程序(如文本编辑器)也可对文件进行操作.
<<file or Get["file"] | 读取 Wolfram 语言输入文件,返回文件中最后一个表达式 |
FilePrint["file"] | 显示文件的内容 |
expr>>file or Put[expr,"file"] | 将表达式写入文件 |
expr>>>file or PutAppend[expr,"file"] | 将表达式追加到文件中 |
如果 Wolfram 语言找不到要求读取的文件,会显示一条消息,然后返回符号 $Failed:
用 <<file 读取文件时,Wolfram 语言将返回它在文件中运行的最后一个表达式. 通过在文件中最后一个表达式的末尾添加分号,或在该表达式之后添加 Null,可以避免读取文件时出现任何可见的结果.
如果 Wolfram 语言在读取文件时遇到语法错误,它将报告该错误,跳过文件的其余部分,然后返回 $Failed. 如果语法错误发生在使用 BeginPackage 和其他上下文操作函数的软件包中,则 Wolfram 语言会尝试将上下文恢复到软件包被读取之前的状态.
保存多个 Wolfram 语言表达式
Wolfram 语言输入文件可以包含任意数量的表达式. 但是,每个表达式必须从新的一行开始. 表达式可以根据需要连续占据多行. 就像在标准的交互式 Wolfram 语言会话中一样,表达式一旦完成就立即对其进行处理. 请注意,在文件中,与交互式会话不同,可以在任何位置插入空白行而不会产生任何影响.
当 expr>>>file 时,Wolfram 语言会将你给出的每个新表达式附加到文件末尾. 如果使用 expr>>file,Wolfram 语言会清除掉文件中以前的所有内容,然后将 expr 放入文件中.
以不同的格式保存 Wolfram 语言表达式
当你用 >> 或 >>> 将表达式写入文件时,通常以 Wolfram 语言输入格式给出表达式,以便可以将其读回 Wolfram 语言中. 但是,有时你可能希望以其他格式保存表达式. 可以通过在表达式周围添加格式指令(如 OutputForm)来完成.
保存 Wolfram 语言对象的定义
使用文件的最常见原因之一是保存 Wolfram 语言对象的定义,以便能够在后续的 Wolfram 语言会话中再次读取它们. 运算符 >> 和 >>> 使你可以将 Wolfram 语言表达式保存在文件中. 可以使用函数 Save 以适合在后续 Wolfram 语言会话中执行的格式保存 Wolfram 语言对象的完整定义.
Save["file",symbol] | 将符号的完整定义保存在文件中 |
Save["file","form"] | 保存名称与字符串模式 form 匹配的符号的定义 |
Save["file","context`"] | 保存指定上下文中所有符号的定义 |
Save["file",{object1,object2,…}] | 保存几个对象的定义 |
在 Wolfram 语言中定义新对象时,通常定义会依赖于之前定义的其他对象. 如果想在后续的 Wolfram 语言会话中重建新对象的定义,那么不仅要保存对象自身的定义,还要保存它所依赖的其他对象的定义. 函数 Save 会查看你要求保存的对象的定义,并自动保存所依赖的其他对象的所有定义. 但是,为了避免保存大量不必要的内容,Save 从来不包含有 Protected 属性的符号的定义. 它假定这些符号的定义是内置的. 但是,在保全这些定义的情况下,将 Save 生成的输出读回到新的 Wolfram 语言会话中将始终能建立对象的定义,该定义应与之前的定义完全一样.
以编码形式保存 Wolfram 语言定义
创建用于输入到 Wolfram 语言的文件时,通常希望它们只包含可以直接进行读取或修改的“纯文本”内容. 但是,有时可能希望对文件的内容进行“编码”,使得它们不能直接作为纯文本被读取或修改,但可以被加载到 Wolfram 语言中. 可以使用 Wolfram 语言函数 Encode 创建被编码过的文件.
Encode["source","dest"] | 将文件 source 被编码过的版本写入文件 dest |
<<dest | 读入编码文件 |
Encode["source","dest","key"] | 用指定密钥进行编码 |
Get["dest","key"] | 读入用密钥编码过的文件 |
Encode["source","dest",MachineID->"ID"] | 创建只能在有特定 ID 的计算机上读取的编码文件 |
DumpSave["file.mx",symbol] | 用 Wolfram 语言内部格式保存符号的定义 |
DumpSave["file.mx","context`"] | 保存上下文中所有符号的定义 |
DumpSave["file.mx",{object1,object2,…}] | 保存几个符号或上下文的定义 |
DumpSave["package`",objects] | 用专门选择的名称将定义保存到文件中 |
如果必须读入非常大或复杂的定义,通常会发现以 Wolfram 系统内部格式(而不是文本)保存这些定义会更高效. 可以使用 DumpSave 来完成.
<< 可识别出文件内包含 Wolfram 系统内部格式的定义,并相应地进行操作. 微妙之处在于,Wolfram 系统内部格式因计算机系统而异,在一台计算机上创建的 .mx 并不能总是在另一台计算机上读取.
如果使用 DumpSave["package`",…],则 Wolfram 语言会将定义写入到名称为 package.mx/system/package.mx 的文件中,其中 system 标志着计算机系统的类型.
在大多数计算机系统上,可以从 Wolfram 语言中执行外部程序或命令. 通常,你希望将在 Wolfram 语言中生成的表达式发送到外部程序,或者从外部程序中获取结果,并将其读入 Wolfram 语言中.
结构化通信的要点是与专门为处理此类对象而设置的外部程序交换完整的 Wolfram 语言表达式. 结构化通信的基础是Wolfram Symbolic Transfer Protocol (WSTP) 系统,“WSTP 和外部程序的通讯”对此进行了论述.
<<file | 读入文件 |
<<"!command" | 运行外部指令,并读入产生的输出 |
expr>>"!command" | 将文本形式的 expr 提供给外部指令 |
ReadList["!command",Number] | 运行外部指令,并读入它产生的数字列表 |
总的来说,无论在哪里使用普通文件名,Wolfram 语言允许你给出一个管道,以外部命令的形式编写,在前面加上一个感叹号就可以了. 使用管道时,Wolfram 语言将执行外部指令,并从中发送或接收文本.
In[1]:= !squares 4
1 1
2 4
3 9
4 16
Wolfram 语言中的管道为与外部程序的非结构化通信提供了非常通用的机制。在许多计算机系统上,Wolfram 语言管道是使用底层操作系统中的管道机制来实现的;但是,在某些情况下,将使用其他进程间通信机制. Wolfram 语言中非结构化通信的一个限制是,给定管道只能用于输入或输出,不能同时用于两者. 为了进行真正的双向通信,需要使用 WSTP.
OpenWrite[] | 在你的计算机系统上,在临时文件的默认区域中打开一个具有唯一名称的新文件 |
特别是在使用临时文件时,你可能会发现能够运行不显式发送或接收来自 Wolfram 语言的数据的外部指令很有用. 可以通过 Wolfram 语言函数 Run 来完成.
Run["command",arg1,…] | 在 Wolfram 语言内部运行外部指令 |
重要的是要认识到 Run 永远不会“捕获”外部指令的任何输出. 因此,输出到哪里完全由操作系统决定. 同样,Run 不为外部指令提供输入. 这意味着命令可以通过操作系统提供的任何机制获取输入. 有时外部命令可能能够访问 Wolfram 语言本身使用的输入和输出流. 在某些情况下,这可能正好就是你想要的. 但是,如果你是在前端使用 Wolfram 语言,这样做则可能会造成很大的麻烦.
RunThrough["command",expr] | 运行 command,用 expr 作为输入,将输出读回 Wolfram 语言 |
函数 RunThrough 将表达式文本写入临时文件,然后将该文件作为输入提供给外部程序,并抓取输出作为 Wolfram 语言的输入. 注意,在 RunThrough 中,和 Run 一样,不要在指令前加感叹号.
SystemOpen["target"] | 用计算机系统上关联的程序打开指定文件、URL 或其他目标 |
SystemOpen 使用操作系统中的设置来确定如何打开 URI 或文件. 打开文件时,它通常使用与双击文件图标时使用的程序相同的程序.
可以将 >> 和 << 视为“高级”的 Wolfram 系统输入输出功能. 它们基于一组直接与流一起使用的较低级别的输入输出基元. 通过使用这些基元,可以对 Wolfram 系统怎样执行输入和输出进行更精确的控制. 例如,如果编写从文件或管道中存储或获取中间数据的 Wolfram 系统程序,通常需要使用这些基元.
Wolfram 系统中将输出写入流中的基本底层方案如下:首先,调用 OpenWrite 或 OpenAppend “打开流”,告诉 Wolfram 系统将输出写入特定的文件或外部程序,并指定以什么形式写入输出. 打开流后,可以调用 Write 或 WriteString 将表达式或字符串序列写入流. 完成后,请调用 Close “关闭流”.
"name" | 由名称指定的文件 |
"!name" | 由名称指定的指令 |
InputStream["name",n] | 输入流 |
OutputStream["name",n] | 输出流 |
OpenWrite["file"] | 打开到文件的输出流, 清除文件中以前的内容 |
OpenWrite[] | 打开到新的临时文件的输出流 |
OpenAppend["file"] | 打开到文件的输出流,追加到文件中已有内容的后面 |
OpenWrite["!command"] | 打开到外部指令的输出流 |
Write[stream,expr1,expr2,…] | 将表达式序列写入流,以换行符结束输出 |
WriteString[stream,str1,str2,…] | 将字符串序列写入流,不加换行符 |
Close[stream] | 告诉 Wolfram 系统不再使用流 |
调用 Write[stream,expr] 时,将表达式写入指定的流. 默认是用 Wolfram 系统输入形式写入表达式.如果调用 Write 写入表达式序列,它将把这些表达式一个接一个地写入流中. 通常,相邻表达式之间不留空格. 但是,写完所有表达式后,Write 总是以换行符结束输出.
以输入格式写入所有表达式. 一个 Write 指令中的表达式被写在同一行上:
Write 提供了一种写出完整 Wolfram 语言表达式的方式. 但是,有时你可能希望写出结构化程度较低的数据. WriteString 允许写出任何字符串. 与 Write 不同,WriteString 不添加换行符或其他字符.
Write[{stream1,stream2},expr1,…] | 将表达式写入一组流 |
WriteString[{stream1,stream2},str1,…] | 将字符串写入一组流 |
在标准的交互式 Wolfram 系统会话中,通常会定义几个输出通道. 它们指定应将特定种类的输出发送到何处. 因此,例如,$Output 指定标准输出应去到何处,而 $Messages 指定消息应去到何处. 函数 Print 实际上就是通过调用 Write 和 $Output 通道来完成. 同样,通过调用 Write 和 $Messages 通道可完成 Message. “主循环”列出了典型 Wolfram 系统会话中使用的通道.
请注意,通过 Wolfram Symbolic Transfer Protocol (WSTP) 运行 Wolfram 系统时,通常会使用其他方法. 通常将所有输出写入单个 WSTP 链接,但是每条输出都显示在表明其类型的“数据包”中.
特殊流 "stdout" 使你可以将输出提供给操作系统提供的“标准输出”. 但是请注意,只能将此流与 Wolfram 系统的基于文本的简单接口一起使用. 如果与 Wolfram 系统的交互比较复杂,则此流将无法工作,并且使用它可能会造成很大的麻烦.
选项名称
|
默认值
| |
FormatType | InputForm | 使用的默认输出格式 |
PageWidth | 78 | 页面的宽度,以字符数给出 |
NumberMarks | $NumberMarks | 是否在近似数字中包含 ` 标记 |
CharacterEncoding | $CharacterEncoding | 用于特殊字符的编码 |
打开一个流,指定使用的默认输出格式应为 OutputForm:
以 OutputForm 将表达式写入流:
选项 PageWidth 给出可用于 Wolfram 系统文本输出的页面宽度. 输出的所有行都已被断开以适应该宽度. 如果不想断开输出行,可以设置 PageWidth->Infinity. 但是,通常你会希望将 PageWidth 设为一个适合于特定输出设备的值. 在许多系统上,必须运行一个外部程序来找出该值. 通过 SetOptions,可对 PageWidth 的默认规则进行设置,如 PageWidth:><<"!devicewidth",以便自动运行外部程序来求出选项的值.
选项 CharacterEncoding 允许指定一种字符编码,用来指定通过 Write 或 WriteString 发送到特定输出流的所有字符串所用的编码. 如果要修改国际字符集或防止出现特定的输出设备接收其无法处理的字符的情况,通常需要使用 CharacterEncoding.
Options[stream] | 给出为流设置的选项 |
SetOptions[stream,opt1->val1,…] | 重置处于打开状态的流的选项 |
改变打开的流的 FormatType 选项:
Options 显示为打开的流设置的选项:
在会话的所有时刻,Wolfram 系统都会维护当前打开的所有输入和输出流的列表 Streams[] 及其选项. 在某些情况下,你可能会发现直接查看此列表很有用. 但是,Wolfram 系统不允许你修改列表,除非通过 OpenRead 等间接进行操作.
目录操作
Directory[] | 当前工作目录 |
SetDirectory["dir"] | 设定当前工作目录 |
ResetDirectory[] | 恢复到以前的工作目录 |
调用 SetDirectory 时,可以给出操作系统可以识别的任何目录名称. 举例来说,在基于 Unix 的系统上,可以使用 .. 符号指定目录层次结构中上一级的目录,用 ~ 指定 "home" 目录.
每次使用 SetDirectory 转到新目录时,Wolfram 语言始终会记住先前的目录. 可以使用 ResetDirectory 返回到先前的目录. 通常,Wolfram 语言会一直保留由 DirectoryStack[] 提供的目录堆栈. 每次调用 SetDirectory 时,都会向堆栈中添加一个新目录,而每次调用 ResetDirectory 时,从堆栈中删除一个目录.
ParentDirectory[] | 当前工作目录的父目录 |
$InitialDirectory | Wolfram 系统启动时的初始目录 |
$HomeDirectory | 家目录,如果有定义的话 |
$BaseDirectory | Wolfram 系统要加载的整个系统文件的基本目录 |
$UserBaseDirectory | Wolfram 系统要加载的用户特定文件的基本目录 |
$InstallationDirectory | Wolfram 系统安装程序所在的顶层目录 |
查找文件
Wolfram 语言会扫描文件的全名,并查看其中是否包含 *、$、~、?、[、"、∖ 和 ' 等任何“元字符”. 如果找到了此类字符,则将全名传递给操作系统或壳进行解释. 这意味着,如果使用的是基于 Unix 的系统,就将 name* 和 $VAR 这样的构建展开. 一般情况下,Wolfram 语言接受操作系统或壳返回的所有内容,并将其视为完整的文件名.
但是,如果尝试从文件获取输入,那么 Wolfram 语言会进行另一轮处理. 所发生的情况是,Wolfram 语言会查看用于确定与搜索文件相关的目录名称的函数的 Path 选项的值. Path 选项的默认设置是全局变量 $Path.
通常,全局变量 $Path 被定义为字符串列表,每个字符串表示一个目录. 每次需要输入文件时,Wolfram 语言实际上是暂时将这些目录依次转换为当前工作目录,然后从该目录中尝试查找你请求的文件.
也可以使用 FindFile 查找文件.
FindFile["name"] | 查找将由 Get 和相关函数加载的具有指定名称的文件 |
FileExistsQ["name"] | 确定文件是否存在 |
查找 $Path 上的文件.
对软件包名称应用 FindFile 将返回该软件包中 init.m 文件的绝对名称.
列出目录的内容
FileNames[] | 列出当前工作目录中的所有文件 |
FileNames["form"] | 列出当前工作目录中文件名与字符串模式 form 匹配的所有文件 |
FileNames[{"form1","form2",…}] | 列出文件名与任一 formi 匹配的所有文件 |
FileNames[forms,{"dir1","dir2",…}] | 给出名称与任一目录 diri 下的 forms 匹配的所有文件的全名 |
FileNames[forms,dirs,n] | 包括子目录中的文件,最多 n 层以下 |
FileNames[forms,dirs,Infinity] | 包括所有子目录中的文件 |
FileNames[forms,$Path,Infinity] | 给出名称与 $Path 中的目录的任何子目录中的 forms 匹配的所有文件 |
FileNames 返回与文件名相对应的字符串列表. 当返回的文件不在当前目录中时,给出相对于当前目录的文件名. 注意,所有名称均以适用于特定计算机系统的格式给出.
组成文件名
DirectoryName["file"] | 从文件名中提取目录名 |
FileNameJoin[{"directory","name"}] | 从目录名和文件名组合完整的文件名 |
ParentDirectory["directory"] | 给出目录的父目录 |
FileNameJoin[{"dir1","dir2",…,"name"}] | 根据目录名称的层次结构组合完整的文件名 |
FileNameJoin[{"dir1","dir2",…}] | 根据目录名称的层次结构组合出一个目录名称 |
应该意识到,不同的计算机系统可能会以不同的方式给出文件名. 例如,Windows 系统通常以 dir:∖dir∖dir∖name 形式给出名称,而 Unix 系统以 dir/dir/name 形式给出名称. 函数 FileNameJoin 以适合你所使用的特定计算机系统的适当方式组合文件名.
FileNameSplit["name"] | 将名称拆分为目录和文件名列表 |
FileNameTake["name",…] | 提取文件名 |
FileNameDrop["name",…] | 去掉文件名的部分内容 |
FileNameDepth["name"] | 获取文件名中路径元素的数量 |
$PathnameSeparator | 操作系统中使用的路径名分隔符 |
利用诸如 FileNameSplit 和 FileNameJoin 这样的函数可对文件名进行其他操作. 他们遵循操作系统使用的文件名分隔符,适当地拆分文件名. 默认情况下,FileNameJoin 将使用 $PathnameSeparator 以适合你的操作系统的规范形式生成名称.
如果设置了相关文件的集合,那么在读取一个文件时通常可以很方便地引用另一个文件. 全局变量 $InputFileName 给出当前正在从中获取输入的文件的名称. 然后,通过 DirectoryName 和 FileNameJoin 可以方便地指定其他相关文件的名称
$InputFileName | 当前正在从中获取输入的文件的名称 |
在 Wolfram 语言中处理文件的一个问题是对于不同的计算机系统,文件和目录名称的形式有所不同. 这意味着在不同系统上,含有标准 Wolfram 语言软件包的文件名可能会完全不同. 通过一系列约定,可以在所有系统上使用相同的命令读取标准 Wolfram 语言软件包. 解决办法就是每个程序包都定义一个所谓的 Wolfram 语言上下文,形式为 name`name`. 在每个系统上,所有文件都根据它们定义的上下文进行命名. 然后,当你使用命令 <<name`name` 时,Wolfram 语言会自动将上下文名称转换为适合你的特定计算机系统的文件名.
标准的文件扩展名
file.m | 纯文本格式的 Wolfram 语言表达式文件 |
file.nb | Wolfram 系统笔记本文件 |
file.mx | DumpSave 格式的 Wolfram 语言定义 |
FileBaseName["name"] | 文件名,不包括扩展名 |
FileExtension["name"] | 文件名的扩展名 |
但是,在 Wolfram 系统笔记本中,可以使用前端的单元菜单将某些单元标识为初始化单元,则每次打开笔记本时,系统都会自动对这些单元的内容进行计算.
单元方括号中的 I 表示第二个单元是一个初始化单元,每次打开笔记本时,系统都会对其进行计算.
有时,同时将 Wolfram 资源放在包含说明文字的笔记本中和只含有原始 Wolfram 语言定义的软件包中会为维护带来方便. 可以通过将 Wolfram 语言定义放入笔记本的初始化单元中来完成. 每次保存笔记本时,前端都将允许你保存一个只包含原始 Wolfram 语言定义的关联的 .m 文件.
CopyFile["file1","file2"] | 将文件 file1 复制为 file2 |
RenameFile["file1","file2"] | 将文件 file1 命名为 file2 |
DeleteFile["file"] | 删除文件 |
FileByteCount["file"] | 给出文件的字节数 |
FileDate["file"] | 给出文件的修改日期 |
SetFileDate["file"] | 将文件的修改日期设为当前日期 |
FileType["file"] |
注意,CopyFile 和 RenameFile 将最终文件的修改日期设为与原始文件相同. FileDate 以 DateList 使用的{year,month,day,hour,minute,second} 格式返回修改日期.
CreateDirectory["name"] | 创建新目录 |
DeleteDirectory["name"] | 删除空目录 |
DeleteDirectory["name",DeleteContents->True] | 删除目录及其包含的所有文件和目录 |
RenameDirectory["name1","name2"] | 重命名目录 |
CopyDirectory["name1","name2"] | 复制目录及其中的所有文件 |
通过 << 可以读取包含以输入形式给出的 Wolfram 语言表达式的文件. 但是,有时你可能需要读取其他格式的数据文件. 例如,可能是由外部程序生成的数据,由一系列用空格分隔的数字组成. 不能按 Wolfram 语言输入直接读取这些数据. 但是,函数 ReadList 可以从文件或输入流中获取此类数据,并将其转换为 Wolfram 语言列表.
ReadList["file",{Number,Number}] | 从文件中读取数字,将相邻的一对数字放入单独的列表 |
ReadList["file",Table[Number,{n}]] | 将相邻的 n 个数字放入单独的列表 |
ReadList["file",Number,RecordLists->True] | |
将文件的每一行的所有数字放入单独的列表 |
含有以类似 Fortran 的 "E" 表示法给出的数字的文件:
ReadList 可处理这种格式的数字:
Byte | 数据的一个字节,以整数形式返回 |
Character | 一个字符,以一个字符的字符串形式返回 |
Real | 以类似 Fortran 的表示法给出的近似数 |
Number | 以类似 Fortran 的表示法给出的精确数或近似数 |
Word | 由单词分隔符分隔的字符序列 |
Record | 由记录分隔符分隔的字符序列 |
String | 以换行符结束的字符串 |
Expression | 完整的 Wolfram 语言表达式 |
Hold[Expression] | 完整的 Wolfram 语言表达式,在 Hold 内返回 |
ReadList 允许从文件中读取“单词”. 它认为“单词”是由单词分隔符分隔的任何字符序列. 可以设置选项 WordSeparators 来指定用作单词分隔符的字符串. 默认值为包含空格和制表符,但不包含标准的标点符号. 请注意,在所有情况下,相邻的单词都可以由任意数量的单词分隔符分隔. ReadList 返回的实际单词中不会包括这些分隔符.
选项名称
|
默认值
| |
RecordLists | False | 是否将每个记录中的对象单独列出 |
RecordSeparators | {"\r\n", "\n","\r"} | 记录分隔符 |
WordSeparators | {" ","∖t"} | 单词分隔符 |
NullRecords | False | 是否保留长度为零的记录 |
NullWords | False | 是否保留长度为零的单词 |
TokenWords | {} | 作为标记的单词 |
ReadList 的选项.
Wolfram 语言认为任何数据文件都是由一系列记录组成的. 默认情况下,每一行都被视为一条单独的记录. 通常,你可以设置选项 RecordSeparators 给出一系列记录分隔符. 请注意,单词永远不能越过记录分隔符. 与单词分隔符一样,相邻记录之间可以存在任意数量的记录分隔符,这些分隔符不被视为记录本身的一部分.
ReadList["file",Record,RecordSeparators->{}] | |
将整个文件作为一个字符串读入 | |
ReadList["file",Record,RecordSeparators->{{"lsep1",…},{"rsep1",…}}] | |
将文件中位于 lsepi 和 rsepi 之间的部分放入列表中 |
RecordSeparators 选项的设置.
Wolfram 语言通常允许相邻的记录或单词之间出现任意数量的分隔符. 但是,当出现多个分隔符时,你可能希望假设在每对相邻的分隔符之间出现了“空记录”或“空单词”. 可以通过设置选项 NullRecords->True 或 NullWords->True 来实现.
大多数情况下,我们都希望用不被视为单词的分隔符来分隔单词. 但是,有时允许用特殊的“标记单词”(它们本身就是单词)来分隔单词是会带来很多方便. 可以将此类标记单词的列表作为选项 TokenWords 的设置.
可以使用 ReadList 从文件中读取 Wolfram 语言表达式. 通常,每个表达式都必须以换行符结尾,尽管单个表达式可能会持续多行.
ReadList 可以将其读取的对象插入任何 Wolfram 语言表达式中. ReadList 的第二个参数可由包含符号(如 Number 和 Word)的任意表达式组成,这些符号会指定要读取的对象. 例如, ReadList["file",{Number,Number}] 会将其读取的连续数字对插入到列表中. 同样,ReadList["file",Hold[Expression]] 将其读取的表达式放入 Hold 内.
到达文件末尾后,符号 EndOfFile 出现,代替尚未读取的数字:
OpenRead["file"] | 打开文件进行读取 |
OpenRead["!command"] | 打开管道进行读取 |
Read[stream,type] | 从流中读取指定类型的对象 |
Skip[stream,type] | 跳过输入流中指定类型的对象 |
Skip[stream,type,n] | 跳过输入流中指定类型的 n 个对象 |
Close[stream] | 关闭输入流 |
ReadList 允许你读取特定文件或输入流中的所有数据. 但是,有时你希望一次只读取一条数据,先测试一下,知道下面将得到什么样的数据.
当你从文件中读取单项数据时,Wolfram 语言将始终记住你在文件中所处的“当前指针”. 调用 OpenRead 时,Wolfram 语言会设置来自文件的输入流,并将当前指针设为文件的开头. 每次使用 Read 从文件中读取对象时,Wolfram 语言都会将当前指针设为紧接在读取对象之后. 使用 Skip 可以使当前指针跳过一系列对象,不必读取这些对象.
FindList["file","text"] | 获取文件中包含指定文本的所有行的列表 |
FindList["file","text",n] | 获取包含指定文本的前 n 行的列表 |
FindList["file",{"text1","text2",…}] | 获取包含任一 texti 的行 |
默认情况下,FindList 扫描文件中的行,返回含有指定文本的行. 通常,也可以用 FindList 扫描记录,返回包含指定文本的完整记录. 与 ReadList 中一样,选项 RecordSeparators 可以告诉 Wolfram 语言要将哪些字符串视为记录分隔符. 注意,通过将一对列表作为 RecordSeparators 的设置,可以指定不同的左右分隔符. 这样,可以使 FindList 只搜索位于特定分隔符之间的文字.
选项名称
|
默认值
| |
RecordSeparators | {"∖n"} | 记录分隔符 |
AnchoredSearch | False | 是否要求搜索的文字在记录的开头 |
WordSeparators | {" ","∖t"} | 单词分隔符 |
WordSearch | False | 是否要求搜索的文字作为单词出现 |
IgnoreCase | False | 是否将小写和大写字母视为一样 |
FindList 的选项.
只找出文件中 Here 出现在行的开头的句子:
通常,FindList 会找出出现在记录内任何地方的文字. 但是,通过设置选项 WordSearch->True,可以告诉 FindList 要求查找的文字以单独的单词出现在记录中. 选项 WordSeparators 指定单词分隔符的列表.
FindList[{"file1","file2",…},"text"] | 在任一 filei 中查找文字 |
FindList["!command",…] | 运行外部指令,在其输出中查找文字 |
OpenRead["file"] | 打开文件进行读取 |
OpenRead["!command"] | 打开通道进行读取 |
Find[stream,text] | 查找 text 下次出现的地方 |
Close[stream] | 关闭输入流 |
为了使用 Find,首先必须使用 OpenRead 打开输入流. 然后,每次在此流上调用 Find 时,它将搜索指定的文本,并使文件中的当前指针紧跟在找到记录之后. 因此,可以多次调用 Find 查找文字后续出现的地方.
找到第一个含有 And 的行:
Read 读取出现在行首的单词:
StreamPosition[stream] | 找出打开的流中当前指针的位置 |
SetStreamPosition[stream,n] | 设置当前指针的位置 |
SetStreamPosition[stream,0] | 将当前指针的位置设为流的开头 |
SetStreamPosition[stream,Infinity] | 将当前指针的位置设为流的结尾 |
有时,你可能需要知道流中当前指针的位置在哪里,并能够对其进行重置. 在大多数计算机系统上,StreamPosition 以整数形式返回当前指针的位置,即距流的开头的字节数.
现在,Read 返回第一行的剩余部分:
如 Read 和 Find 这样的函数经常被用来处理来自外部文件的文本和数据. 但是,在某些情况下,你可能会发现使用这些函数在 Wolfram 语言中处理字符串也很方便. 可以通过使用函数 StringToStream 来完成,该函数将打开一个输入流,不是从外部文件而是从 Wolfram 语言字符串中读取字符.
StringToStream["string"] | 打开输入流,从字符串中读取 |
Close[stream] | 关闭输入流 |
与字符串关联的输入流的工作方式和与文件关联的输入流完全相同. 在任何给定时间,流中都会有一个当前位置,当使用如 Read 这样的函数时,该位置会前进. 当前位置由函数 StreamPosition[stream] 以距字符串开头的字符数给出. 可以使用 SetStreamPosition[stream,n] 显式设置当前位置.
如果现在尝试从流中读取,将始终得到 EndOfFile:
特别是在处理大量文本数据时,通常会将相当长的字符串读入 Wolfram 语言,然后使用 StringToStream 以便在 Wolfram 语言中进一步处理这些字符串. 使用 StringToStream 创建输入流后,就可以使用在处理文件时提到的任何函数来读取和搜索字符串.
如 Read 和 Write 这样的函数处理普通的可打印文本. 但是,在处理外部数据文件或设备时,有时必须到更低层级,直接处理原始的二进制数据. 可以使用 BinaryRead 和 BinaryWrite 来完成.
BinaryRead[stream] | 读取一个字节 |
BinaryRead[stream,type] | 读取一个指定类型的对象 |
BinaryRead[stream,{type1,type2,…}] | 读取一组对象 |
BinaryWrite[stream,b] | 写入一个字节 |
BinaryWrite[stream,{b1,b2,…}] | 写入字节序列 |
BinaryWrite[stream,"string"] | 写入字符串中的字符 |
BinaryWrite[stream,x,type] | 写入指定类型的对象 |
BinaryWrite[stream,{x1,x2,…},type] | 写入对象序列 |
BinaryWrite[stream,{x1,x2,…},{type1,type2,…}] | |
写入不同类型的对象 |
"Byte" | 8‐bit 不带符号的整数 |
"Character8" | 8‐bit 字符 |
"Character16" | 16‐bit 字符 |
"Complex64" | IEEE 单精度复数 |
"Complex128" | IEEE 双精度复数 |
"Complex256" | IEEE 四精度复数 |
"Integer8" | 8‐bit 有符号的整数 |
"Integer16" | 16‐bit 有符号的整数 |
"Integer32" | 32‐bit 有符号的整数 |
"Integer64" | 64‐bit 有符号的整数 |
"Integer128" | 128‐bit 有符号的整数 |
"Real32" | IEEE 单精度实数 |
"Real64" | IEEE 双精度实数 |
"Real128" | IEEE 四精度实数 |
"TerminatedString" | 以空字符结尾的 8‐bit 字符的字符串 |
"UnsignedInteger8" | 8‐bit 不带符号的整数 |
"UnsignedInteger16" | 16‐bit 不带符号的整数 |
"UnsignedInteger32" | 32‐bit 不带符号的整数 |
"UnsignedInteger64" | 64‐bit 不带符号的整数 |
"UnsignedInteger128" | 128‐bit 不带符号的整数 |
BinaryWrite 自动为文件打开一个流. 将其关闭:
像 Read 和 Write 一样,BinaryRead 和 BinaryWrite 也适用于流. 但是,如果给出文件名,它们会自动将指定的文件作为流打开. 要直接创建流,可以使用 OpenRead 或 OpenWrite. 在某些计算机系统上,如果想要将 BinaryRead 和 BinaryWrite 与流一起使用,必须设置选项 BinaryFormat->True,以防止诸如换行符转换之类的问题造成损坏.
使用 Wolfram 语言时,通常不会接触到计算机内部原始形式的数据. 但是,有了 BinaryRead 和 BinaryWrite,事情就不再是这样了. 由此会带来一个小问题,不同的计算机可能会以不同顺序排列组成数字的字节,这由 $ByteOrdering 的设置决定.
BinaryReadList["file"] | 读取文件的所有字节 |
BinaryReadList["file",type] | 读取所有数据,将其视为某种类型的对象 |
BinaryReadList["file",{type1,type2,…}] | 将数据视为一系列类型的对象 |
BinaryReadList["file",types,n] | 只读取前 n 个对象 |
BinaryRead 和 BinaryWrite 在读写原始二进制数据时有很大的灵活性. 但是在许多实际应用中,人们只想处理特定的预定义格式. 可以使用 Import 和 Export 来执行此操作.
除了许多复杂的格式,Import 和 Export 还支持包含相同数据元素序列的文件,类型与 BinaryRead 和 BinaryWrite 中的相同. 它们还支持由单个二进制位(用 0 或 1 表示)组成的 "Bit" 格式.
如果你有用 C 或 Fortran 编写的专用程序,可能会需要将用 Wolfram 语言生成的公式插入程序的源代码中. Wolfram 语言可帮助你将数学表达式转换为 C 和 Fortran 表达式.
CForm[expr] | 按可以在 C 程序中使用的形式写出 expr |
FortranForm[expr] | 按可以在 Fortran 中使用的形式写出 expr |
Export[file,expr,"C"] | 写出计算 expr 的 C 函数 |
下面是以 C 形式给出的同一个表达式. 在 Wolfram 多数版本都提供的 C 标头文件 mdefs.h 中,定义了对象(如 Power)的宏:
这里,整个 C 函数是从 Wolfram 语言的 CompiledFunction 表达式计算得出的:
将 Wolfram 语言表达式转换为 C 或 Fortran 的常见动机之一是试图更快地进行数值计算. 但是 C 和 Fortran 可能比 Wolfram 语言更有效的一个最重要的原因是,在这些语言中,用户总是预先指定每个变量的类型,即整数、实数、数组等.
Compile[x,expr] | 将表达式编译成高效的内部代码 |
Compile[x,expr,CompilationTarget->"C"] | 编译成 C 代码,并链接回 Wolfram 语言 |
脚本文件
Wolfram 语言脚本是一个包含 Wolfram 语言指令的文件,用户通常可以在 Wolfram 语言会话中按顺序计算这些指令. 如果需要多次重复这些指令,编写一个脚本会非常有用. 把这些指令收集在一起确保它们按照特定的顺序计算,并且没有忽略任何指令. 如果要运行复杂的、很长的计算,这么做是很重要的.
对脚本文件的结构没有任何要求. 系统会按顺序读入该文件中给出的任何 Wolfram 语言指令序列并进行计算. 如果你的代码比一个普通指令列表更复杂,可以考虑编写一个更为结构化的程序包,如“建立 Wolfram 语言程序包”中所述.
当我们不需要用一个交互式的会话时,即你的脚本封装了需要执行的单个计算时,Wolfram 语言脚本会更有用. 例如,计算中涉及繁重的计算任务(如线性代数、优化、数值积分或微分方程的解),并且你不使用排版功能、动态交互或笔记本的时候 .
脚本可以存储在一般的 .wl 程序包文件或专门的 .wls 脚本文件中. 两种文件的内容是一样的:一系列 Wolfram 语言表达式,起始处带有可选的 "shebang" 行,一般用在类似于 Unix 的操作系统(参见 Unix 脚本可执行文件)中. 文件类型中的唯一不同是它们被双击时的行为. 双击程序包文件会在笔记本程序包编辑器中打开文件;双击脚本文件会执行文件,如果操作系统支持的话. 这在 Windows 上特别有用,因为在 Windows 上不可能将程序与特殊文件相关联,只能与文件扩展名关联. 可以在笔记本界面上编辑脚本文件,但是必须使用 文件 ▶ 打开 来打开.
在本地内核中运行脚本
$ "%ProgramFiles%\Wolfram Research\Mathematica\{First[{}]}\wolfram" -script file.wl
$ /Applications/Mathematica.app/Contents/MacOS/wolfram -script file.wl
$ wolfram -script file.wl
-script 命令行选项指定在特殊的脚本内运行 Wolfram 语言内核,或者以批处理模式运行. 在这种模式下,内核读入指定的文件,并且按顺序计算指令. 通过把输出函数的 PageWidth 选项设为 Infinity,内核关闭默认的换行功能,并且不显示 In[] 和 Out[] 标签. 当在该模式下运行时,标准的输入和输出通道 、 和 不会被重定向,数值按 InputForm 进行格式化.
运行带有 -script 选项的 wolfram 等价于用 Get 指令读入文件,唯一的不同之处是:在计算文件中最后一个指令之后,内核停止运行. 这种行为可能会影响 Wolfram Symbolic Transfer Protocol (WSTP) 连接或者通过运行脚本创建的外部进程.
用 WolframScript 运行脚本
$ wolframscript -file file.wl
WolframScript 会找到最佳的本地内核运行脚本. 如果没有找到任何本地内核,它会连接至云端并在那里运行脚本. 程序接受各种标志以便控制使用本地内核还是云端内核进行计算. 它还设置脚本参数,允许脚本基于启动的形式或收到的输入来改变行为. 使用 WolframScript 的另一个好处是,输入和输出全部被缓存,允许对其应用各种变换. WolframScript 页面对这些额外选项进行了说明,并提供了范例.
在 Windows 和 Linux 上,WolframScript 一般与 Wolfram 系统一起安装. 在 Mac 上,必须运行与 Wolfram 系统绑定的 "Extras" 安装器才能得到 WolframScript. 默认情况下,这些安装器会把 wolframscript 放在 PATH 中.
Unix 脚本可执行文件
类似于 Unix 的操作系统,以及 Windows 中的 Unix 环境(如 cygwin 和 MinGW),允许编写可执行的脚本文件,可按普通的可执行程序那样运行. 这可以通过在文件开头放上一个“解释器”行实现. 也可对包含 Wolfram 语言指令的脚本进行同样的操作.
“解释器”行包括两个字符 #!,它必须是文件中的前两个字符,然后是可执行文件的绝对路径以及其他参数. 为了达到跨平台和机器的最大的兼容性,建议通过如下所示的帮助器 /usr/bin/env 启动 WolframScript. env 程序会找到 PATH 中的 wolframscript 并启动.
#!/usr/bin/env wolframscript
(* generate high-precision samples of a mixed distribution *)
Print /@ RandomVariate[MixtureDistribution[
{1,2},
{NormalDistribution[1,2/10],
NormalDistribution[3,1/10]}],
10, WorkingPrecision -> 50]
$ chmod a+x script.wls
$ ./script.wls
解释器行可能另外包含解释器的其他参数. WolframScript 页面上指定了可能的参数.
#!/usr/bin/env wolframscript -linewise -format XML
Wolfram 语言脚本无需含有 .wl 或 .wls 扩展名. 可执行脚本是与 Unix 操作系统中的任何其他程序等效的功能完整的程序,所以可以在其他脚本、管道中使用,或受控运行等. 每个 Wolfram 语言脚本启动自己的 WolframKernel 拷贝,并且不共享变量或者定义. 注意:同步运行 Wolfram 语言脚本可能受许可证的限制,即可以同时运行多少个内核.
Windows 上的脚本
独立的脚本也可以用在 Windows 上. 不像 Unix 类的操作系统,这些脚本必须含有扩展名 .wls 才能被识别为 Wolfram 语言脚本. 可以从 Windows 浏览器中通过双击启动脚本,也可以在命令提示符后键入名称启动. 而 Unix 解释器行,如果出现的话,会被这种启动机制忽略.
> file.wls
在命令提示符中,可在文件名后传递其他参数. WolframScript 本身看不到这些参数,但是会以参数形式传递给脚本,下一章节会详细描述.
> file.wls arg1 arg2
脚本参数
当运行一个 Wolfram 语言脚本时,可能常常想要通过在命令行上指定参数来修改脚本的行为. Wolfram 语言代码可以通过 $ScriptCommandLine 访问传递给 Wolfram 语言脚本的参数. 另外,可将标准输入的内容作为变量 $ScriptInputString 中的字符串来处理.
$ScriptCommandLine | 启动脚本的命令行 |
$ScriptInputString | 给予脚本的标准输入的内容 |
#!/usr/bin/env wolframscript
(* generate "num" samples of a mixed distribution *)
num = ToExpression[$ScriptCommandLine[[2]]];
Print /@ RandomVariate[
MixtureDistribution[
{1, 2},
{NormalDistribution[1, 0.2],
NormalDistribution[3, 0.1]}
], num, WorkingPrecision -> 50]
$ ./file.wls 10
> file.wls 10
访问脚本时,$ScriptCommandLine 是一个列表,其中,脚本名称为第一个元素,其余元素为命令行参数. $ScriptCommandLine 遵循标准的 argv[] 约定. 注意这完全隐藏了到解释器的路径或 #! 行传递的任何参数.
由于类似 Unix 操作系统执行脚本的方式,仅当通过 wolframscript 调用 Wolfram 语言内核时,$ScriptCommandLine 才被设置为非空列表. 如果打算以批处理模式和标准 Unix 脚本模式运行脚本,可通过计算 $ScriptCommandLine==={} 确定当前的执行模式. 然后,应使用 $ScriptCommandLine 或 $CommandLine 来访问命令行参数.