Erlang虚拟机在一定程度上支持Unicode的文件名。根据VM的启动方式(使用参数 + fnu或+ fnl),给定的文件名可以包含大于255的字符,VM系统会将文件名来回转换为本地文件名编码。
Unicode字符转换的默认行为取决于底层操作系统/文件系统强制执行一致命名的程度。在确保所有文件名都采用一种或另一种编码的操作系统上,Unicode是默认值(目前这适用于Windows和MacOSX)。在具有完全透明文件命名的操作系统上(即除MacOSX以外的所有Unix),ISO-latin-1文件命名是默认的。ISO-latin-1默认的原因是文件名不能保证可以根据预期的Unicode编码进行解释(即UTF-8),并且不能解码的文件名只能通过使用“raw文件名“,其他文件名称为二进制文件。
由于文件名通常不是Erlang中的二进制文件,因此需要转换需要处理原始文件名的应用程序,这就是为什么文件名的Unicode模式在具有完全透明文件命名的系统上不是默认值。
原始文件名是OTP R14B01中的一项新功能,它允许用户将完全未解释的文件名提供给底层操作系统/文件系统。它们以二进制文件形式提供,用户可以根据环境提供正确的编码。函数file:native_name_encoding()可用于检查虚拟机正在工作的编码。如果该函数返回latin1文件名不会以任何方式转换为Unicode,如果它是utf8,如果原始文件名要遵循VM的约定(通常也是OS的约定),则应将其编码为UTF-8。如果您的文件系统具有不一致的文件命名,则使用原始文件名非常有用,其中一些文件以UTF-8编码命名,而其他文件则不以此命名。当虚拟机处于Unicode文件名模式时,这种混合文件名系统上file:list_dir可能会将文件名作为原始二进制文件返回,因为它们不能被解释为Unicode文件名。即使虚拟机未以Unicode文件名翻译模式启动,原始文件名也可用于提供UTF-8编码的文件名。
请注意,在Windows上,即使在Windows上,file:native_name_encoding()也会在默认情况下返回utf8,即使在Windows上也是原始文件名的格式,但底层操作系统特定的代码在小尾数UTF16的限制版本中工作。就Erlang程序员而言,Windows原生Unicode格式是UTF-8 ...
如果VM处于Unicode文件名模式,string()和char() 允许大于255. RawFilename是不受Unicode转换影响的文件名,这意味着它可以包含不符合文件系统期望的Unicode编码的字符尽管虚拟机在Unicode文件名模式下启动,但是不支持UTF-8字符)。
file_info() =
#file_info{size = undefined | integer() >= 0,
type = undefined
| device
| directory
| other
| regular
| symlink,
access = undefined
| read
| write
| read_write
| none,
atime = undefined
| file:date_time()
| integer() >= 0,
mtime = undefined
| file:date_time()
| integer() >= 0,
ctime = undefined
| file:date_time()
| integer() >= 0,
mode = undefined | integer() >= 0,
links = undefined | integer() >= 0,
major_device = undefined | integer() >= 0,
minor_device = undefined | integer() >= 0,
inode = undefined | integer() >= 0,
uid = undefined | integer() >= 0,
gid = undefined | integer() >= 0}
location() = integer()
| {bof, Offset :: integer()}
| {cur, Offset :: integer()}
| {eof, Offset :: integer()}
| bof
| cur
| eof
mode() = read
| write
| append
| exclusive
| raw
| binary
| {delayed_write,
Size :: integer() >= 0,
Delay :: integer() >= 0}
| delayed_write
| {read_ahead, Size :: integer() >= 1}
| read_ahead
| compressed
| {encoding, unicode:encoding()}
file_info_option() = {time, local} | {time, universal} | {time, posix}
导出
advise(IoDevice, Offset, Length, Advise) -> ok | {error, Reason}
Types:
IoDevice = io_device()
Offset = Length = integer()
Advise = posix_file_advise()
Reason = posix() | badarg
posix_file_advise() = normal
| sequential
| random
| no_reuse
| will_need
| dont_need
advise/4可用于宣布将来以特定模式访问文件数据的意图,从而允许操作系统执行适当的优化。
在某些平台上,此功能可能不起作用。
allocate(File, Offset, Length) -> ok | {error, posix()}
Types:
File = io_device()
Offset = Length = integer() >= 0
allocate/3可用于为文件预分配空间。
此功能仅在实现此功能的平台上成功。成功时,为文件预分配空间,但文件大小可能不会更新。这种行为取决于预分配实现。为了保证文件大小更新,必须将文件截断为新的大小。
change_group(Filename, Gid) -> ok | {error, Reason}
Types:
Filename = name_all()
Gid = integer()
Reason = posix() | badarg
更改文件组。请参阅 write_file_info/2。
change_mode(Filename, Mode) -> ok | {error, Reason}
Types:
Filename = name_all()
Mode = integer()
Reason = posix() | badarg
更改文件的权限。请参阅 write_file_info/2。
change_owner(Filename, Uid) -> ok | {error, Reason}
Types:
Filename = name_all()
Uid = integer()
Reason = posix() | badarg
更改文件的所有者和组。请参阅 write_file_info/2。
change_time(Filename, Mtime) -> ok | {error, Reason}
Types:
Filename = name_all()
Mtime = date_time()
Reason = posix() | badarg
更改文件的修改和访问时间。请参阅 write_file_info/2。
close(IoDevice) -> ok | {error, Reason}
Types:
IoDevice = io_device()
Reason = posix() | badarg | terminated
关闭IoDevice引用的文件。它通常会返回正常,预计会出现诸如内存不足等严重错误。
请注意,如果在打开文件时使用了选项delayed_write,则close/1可能会返回旧的写入错误,甚至不会尝试关闭该文件。见open/2。
consult(Filename) -> {ok, Terms} | {error, Reason}
Types:
Filename = name_all()
Terms = [term()]
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
从Filename中读取由'.'分隔的Erlang项 。返回以下内容之一:
{ok,Terms}
该文件已成功读取。
{error,atom()}
打开文件或读取文件时发生错误。有关典型错误代码的列表,请参阅open/2。
{error,{Line,Mod,Term}}
解释文件中的Erlang项时发生错误。使用format_error/1将三元素元组转换为错误的英文描述。
例子:
f.txt: {person, "kalle", 25}.
{person, "pelle", 30}.
1> file:consult("f.txt").
{ok,[{person,"kalle",25},{person,"pelle",30}]}
文件名的编码可以通过epp(3)中描述的注释来设置。
copy(Source, Destination) -> {ok, BytesCopied} | {error, Reason}
copy(Source, Destination, ByteCount) -> {ok, BytesCopied} | {error, Reason}
Types:
Source = Destination = io_device() | Filename | {Filename, Modes}
Filename = name_all()
Modes = [mode()]
ByteCount = integer() >= 0 | infinity
BytesCopied = integer() >= 0
Reason = posix() | badarg | terminated
从Source到Destination复制ByteCount字节 。 Source和Destination是指来自例如open/2的文件名或IO设备。 ByteCount默认为infinity,表示无限数量的字节。
Modes模式是可能模式的列表,请参阅open/2,默认为[]。
如果Source和 Destination都指向文件名,那么这些文件分别以[read,binary] 和[write,binary]作为模式列表的预先打开,以优化副本。
如果Source引用一个文件名,则在拷贝之前以读取模式打开,并在完成时关闭。
如果Destination指向一个文件名,它会在复制之前以模式列表预先以写入模式打开,并在完成时关闭。
返回{ok,BytesCopied},其中BytesCopied是实际复制的字节数,如果在源上遇到文件结尾,则可能小于ByteCount。如果操作失败, 则返回{error,Reason}。
典型的错误原因:至于open/2,如果一个文件必须打开,以及read/2和write/2。
del_dir(Dir) -> ok | {error, Reason}
Types:
Dir = name_all()
Reason = posix() | badarg
尝试删除目录Dir。该目录在被删除之前必须是空的。成功返回ok。
典型的错误原因是:
eacces
Dir的父目录缺少搜索或写入权限。
eexist
该目录不是空的。
enoent
该目录不存在。
enodir
Dir的一个组件不是一个目录。在某些平台上,取而代之的是返回enoent。
einval
尝试删除当前目录。在某些平台上,eacces被返回。
delete(Filename) -> ok | {error, Reason}
Types:
Filename = name_all()
Reason = posix() | badarg
试图删除文件Filename。成功返回ok。
典型的错误原因是:
enoent
该文件不存在。
eacces
对该文件或其父母之一缺少权限。
eperm
该文件是一个目录,用户不是超级用户。
enotdir
文件名的一个组件不是一个目录。在某些平台上,取而代之的是返回enoent。
einval
文件名具有不正确的类型,例如元组。
警告
在未来的版本中,Filename参数的错误类型 可能会生成异常。
eval(Filename) -> ok | {error, Reason}
Types:
Filename = name_all()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
读取和运算由'.'分隔的Erlang表达式(或',',一系列表达式也是一个表达式),来自 Filename。运算的实际结果不返回;文件中的任何表达式序列都必须存在,因为它的副作用。返回以下内容之一:
ok
该文件被读取和运算。
{error,atom()}
打开文件或读取文件时发生错误。有关典型错误代码的列表,请参阅open/2。
{error,{Line,Mod,Term}}
解释文件中的Erlang表达式时发生错误。使用format_error/1将三元素元组转换为错误的英文描述。
文件名的编码可以通过epp(3)中描述的注释来设置。
eval(Filename, Bindings) -> ok | {error, Reason}
Types:
Filename = name_all()
Bindings = erl_eval:binding_struct()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
与eval/1相同,但变量绑定 Bindings用于运算。请参阅 erl_eval(3)关于变量绑定。
file_info(Filename) -> {ok, FileInfo} | {error, Reason}
Types:
Filename = name_all()
FileInfo = file_info()
Reason = posix() | badarg
此功能已过时。 改为使用read_file_info/1,2。
format_error(Reason) -> Chars
Types:
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
Chars = string()
鉴于此模块中任何函数返回的错误原因,请返回英文错误的描述性字符串。
get_cwd() -> {ok, Dir} | {error, Reason}
Types:
Dir = filename()
Reason = posix()
返回{ok,Dir},其中Dir 是文件服务器的当前工作目录。
注意
在极少数情况下,这个函数可能在Unix上失败。如果当前目录的父目录不存在读取权限,则可能发生这种情况。
典型的错误原因是:
eacces
缺少当前目录的其中一个父项的读取权限。
get_cwd(Drive) -> {ok, Dir} | {error, Reason}
Types:
Drive = string()
Dir = filename()
Reason = posix() | badarg
驱动器的格式应为“ Letter:”,例如“c:”。返回{ok,Dir}或 {error,Reason},其中Dir 是指定驱动器的当前工作目录。
该函数在没有当前驱动器概念的平台(例如Unix)上返回{error,enotsup}。
典型的错误原因是:
enotsup
操作系统没有驱动器的概念。
eacces
该驱动器不存在。
einval
云端硬盘的格式无效。
list_dir(Dir) -> {ok, Filenames} | {error, Reason}
Types:
Dir = name_all()
Filenames = [filename()]
Reason = posix()
| badarg
| {no_translation, Filename :: unicode:latin1_binary()}
列出目录中的所有文件,但具有“原始”名称的文件除外。如果成功,返回 {ok,Filenames}。否则,它返回{error,Reason}。 文件名是目录中所有文件名称的列表。名称没有排序。
典型的错误原因是:
eacces
Dir或其父目录之一缺少搜索或写入权限。
enoent
该目录不存在。
{no_translation,Filename}
Filename是一个二进制,其字符在ISO-latin-1中编码,VM以参数+ fnue启动。
list_dir_all(Dir) -> {ok, Filenames} | {error, Reason}
Types:
Dir = name_all()
Filenames = [filename_all()]
Reason = posix() | badarg
列出目录中的所有文件,包括具有“原始”名称的文件。如果成功,返回{ok,Filenames}。否则,它返回{error,Reason}。 文件名是目录中所有文件名称的列表。名称没有排序。
典型的错误原因是:
eacces
Dir或其父目录之一缺少搜索或写入权限。
enoent
该目录不存在。
make_dir(Dir) -> ok | {error, Reason}
Types:
Dir = name_all()
Reason = posix() | badarg
尝试创建目录Dir。缺少父目录不能创建。成功返回ok。
典型的错误原因是:
eacces
Dir的父目录缺少搜索或写入权限。
eexist
已经有一个名为Dir的文件或目录。
enoent
Dir的一个组件不存在。
enospc
设备上没有剩余空间。
enotdir
Dir的一个组件不是一个目录。在某些平台上,取而代之的是返回enoent。
make_link(Existing, New) -> ok | {error, Reason}
Types:
Existing = New = name_all()
Reason = posix() | badarg
在支持链接的平台(Unix和Windows)上建立从Existing到 New的硬链接。如果链接成功创建,该函数返回ok,或 {error,Reason}。在不支持链接的平台上,返回{error,enotsup}。
典型的错误原因:
eacces
缺少Existing或 New的父目录的读取或写入权限。
eexist
新已经存在。
enosup
该平台不支持硬链接。
make_symlink(Existing, New) -> ok | {error, Reason}
Types:
Existing = New = name_all()
Reason = posix() | badarg
在支持符号链接(大多数Unix系统和Windows以Vista开头)的平台上,该函数创建一个文件或目录Existing的新符号链接。 Existing需要不存在。如果链接成功创建,该函数返回ok,或{error,Reason}。在不支持符号链接的平台上, 返回{error,enotsup}。
典型的错误原因:
eacces
缺少Existing或New的父目录的读取或写入权限。
eexist
新已经存在。
enotsup
此平台不支持符号链接。
native_name_encoding() -> latin1 | utf8
此函数返回配置的默认文件名编码以用于原始文件名。通常,提供文件名raw(作为二进制文件)的应用程序应该服从由该函数返回的字符编码。
默认情况下,VM在文件系统和/或使用完全透明文件命名的操作系统上使用ISO-latin-1文件名编码。这包括除MacOSX之外的所有Unix版本,其中vfs层强制执行UTF-8文件命名。通过在启动Erlang时给出实验选项+ fnu,即使对于那些系统,也可以打开文件名的UTF-8转换。如果Unicode文件名翻译生效,只要文件名符合编码,系统就会照常运行,但会返回未正确编码为UTF-8的文件名作为原始文件名(即二进制文件)。
在Windows上,该函数默认返回utf8。操作系统使用纯粹的Unicode命名方案,文件名总是可以解释为有效的Unicode。底层Windows操作系统实际上使用小尾数UTF-16编码文件名的事实可以被Erlang程序员忽略。Windows和MacOSX是虚拟机默认以Unicode文件名模式运行的唯一操作系统。
open(File, Modes) -> {ok, IoDevice} | {error, Reason}
Types:
File = Filename | iodata()
Filename = name_all()
Modes = [mode() | ram]
IoDevice = io_device()
Reason = posix() | badarg | system_limit
以由Modes确定的模式打开文件File,该模式可能包含以下一项或多项内容:
read
该文件必须存在,已打开供阅读。
write
该文件被打开写入。如果它不存在,则创建它。如果该文件存在,并且如果写入未与读取结合,则该文件将被截断。
append
该文件将被打开进行写入,并且如果该文件不存在,该文件将被创建。对使用append打开的文件的每个写入操作都将在文件末尾进行。
exclusive
如果该文件在写入时打开,则该文件如果不存在则创建。如果文件存在,打开将返回 {error,eexist}。
警告
此选项不保证在不支持O_EXCL的文件系统上的排他性,例如NFS。除非您知道文件系统支持该选项,否则不要依赖此选项(通常,本地文件系统应该是安全的)。
raw
该raw选项允许一个文件更快的访问,因为不需要Erlang进程来处理文件。但是,以这种方式打开的文件具有以下限制:
io模块中的功能无法使用,因为它们只能与Erlang进程通信。相反,使用read/2,read_line/1和 write/2 函数。
特别是如果要在原始文件上使用read_line/1,建议将此选项与{read_ahead,Size}选项结合使用,因为面向行的I/O效率不高而不缓冲。
只有打开文件的Erlang进程才能使用它。
远程Erlang文件服务器不能使用; 运行Erlang节点的计算机必须能够访问文件系统(直接或通过NFS)。
binary
当给出这个选项时,对文件的读操作将返回二进制文件而不是列表。
{delayed_write,Size,Delay}
如果使用此选项,则后续write/2调用中的数据将被缓冲,直到至少有Size字节被缓冲,或者直到最早的缓冲数据为Delay毫秒。然后将所有缓冲数据写入一个操作系统调用中。在write/2执行之前,缓存的数据在其他文件操作之前也会被刷新。
此选项的目的是通过减少操作系统调用的数量来提高性能,所以 write/2调用的尺寸应该大大小于Size,并且不会穿插其他许多文件操作,因此会发生这种情况。
使用此选项时,write/2调用的结果可能会过早地报告为成功,并且如果实际发生写入错误,则会将错误报告为下一个文件操作的结果,该操作不会执行。
例如,当使用delayed_write时,经过多次write/2调用后,close/1可能会返回{error,enospc},因为光盘上没有足够的空间用于先前写入的数据,并且可能再次调用close/1因为该文件仍处于打开状态。
delayed_write
与{Delay_write,Size,Delay}相同,使用Size和 Delay的合理默认值。(大约64 KB,2秒)
{read_ahead, Size}
该选项激活读取数据缓冲。如果 read/2调用的字节数大大小于Size字节,则对操作系统的读取操作仍会针对Size 字节块执行。额外的数据被缓冲并在随后的read/2调用中返回,从而减少操作系统调用次数,从而提高性能。
所述read_ahead缓冲器也是高度由利用read_line/1在功能原始模式下,为什么建议该选项(出于性能原因)使用该函数访问原始文件时。
如果read/2调用的大小不小于或大于size字节,则不会获得性能增益。
read_ahead
同为{read_ahead,Size}有一个合理的默认值大小。(大约64 KB)
compressed
使读取或写入gzip压缩文件成为可能。该压缩选项必须以组合读或写,但不能同时使用。请注意,使用read_file_info/1获取的文件大小 很可能与可从压缩文件读取的字节数不匹配。
{encoding, Encoding}
使文件自动转换特定(Unicode)编码中的字符。请注意,提供给file:write或由file:read返回的数据仍然是面向字节的,该选项仅表示数据实际存储在磁盘文件中的方式。
根据编码的不同,读取和写入数据的方法是首选。latin1的默认编码意味着使用这个(文件)模块读取和写入数据,因为这里提供的接口使用面向字节的数据,而使用其他(Unicode)编码使得io(3)模块的get_chars,get_line和put_chars功能更适合,因为它们可以使用完整的Unicode范围。
如果数据以无法转换为指定编码的格式发送到io_device(),或者数据是以无法应对数据字符范围的格式返回数据的函数读取的,则会发生错误,并且该文件将被关闭。
编码的允许值是:
LATIN1
默认编码。提供给ie file:write的字节按原样写入文件,同样从文件读取的字节返回到ie file:read。如果使用io(3)模块进行写入,则该文件只能处理直至代码点255(ISO-latin-1范围)的Unicode字符。
unicode或utf8
在写入文件或从文件中读取字符之前,字符会转换为UTF-8编码或从UTF-8编码转换而来。只要没有存储在文件中的数据超出ISO-latin-1范围(0..255),以这种方式打开的文件就可以使用file:read函数读取,但如果数据包含Unicode超出该范围的码点。该文件最好使用支持Unicode的io(3)模块中的函数进行读取 。
在实际存储到磁盘文件之前,通过任何方式写入文件的字节都会转换为UTF-8编码。
utf16或{utf16,big}
像unicode一样工作,但是可以在大端的UTF-16而不是UTF-8上进行翻译。
{UTF16,little}
像unicode一样工作,但翻译是通过小端UTF-16而不是UTF-8完成的。
utf32或{utf32,big}
像unicode一样工作,但是可以在大端的UTF-32而不是UTF-8上进行翻译。
{UTF32,little}
像unicode一样工作,但是可以使用小端UTF-32而不是UTF-8进行翻译。
编码可以通过使用io:setopts/2函数为“即时”文件进行更改,为什么可以使用latin1编码对文件进行分析,例如BOM,位于BOM之后,然后设置为正确的编码进一步阅读。参见unicode(3)模块了解BOM的功能。
原始文件不允许使用此选项。
ram
文件必须是iodata()。返回一个fd(),它使文件模块对内存中的数据进行操作,就像它是一个文件一样。
返回:
{ok,IoDevice}
该文件已在请求的模式下打开。 IoDevice是对该文件的引用。
{error, Reason}
该文件无法打开。
IoDevice实际上是处理文件的过程的pid。这个过程与最初打开文件的过程相关联。如果IoDevice链接的任何进程终止,则文件将被关闭,进程本身将被终止。从此调用返回的IoDevice可用作IO函数的参数(请参阅 io(3))。
注意
在以前版本的文件,模式都给出一个原子read,write,或read_write,而不是一个列表。出于向后兼容的原因,这仍然是允许的,但不应该用于新代码。另请注意,模式列表中不允许使用read_write。
典型的错误原因:
enoent
该文件不存在。
eacces
缺少权限读取文件或搜索其中一个父目录。
eisdir
指定的文件不是常规文件。它可能是一个目录,一个fifo或一个设备。
enotdir
文件名的一个组件不是一个目录。在某些平台上,取而代之的是返回enoent。
enospc
设备上没有剩余空间(如果 指定了写访问权限)。
path_consult(Path, Filename) -> {ok, Terms, FullName} | {error, Reason}
Types:
Path = [Dir]
Dir = Filename = name_all()
Terms = [term()]
FullName = filename_all()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
搜索路径Path(目录名称列表),直到找到文件Filename。如果文件名是绝对文件名,则路径被忽略。然后从文件中读取用'.'分隔的Erlang项。返回以下内容之一:
{ok,Terms,FullName}
该文件已成功读取。FullName是文件的全名。
{error,enoent}
该文件无法在Path中的任何目录中找到 。
{error,atom()}
打开文件或读取文件时发生错误。有关典型错误代码的列表,请参阅open/2。
{error,{Line,Mod,Term}}
解释文件中的Erlang项时发生错误。使用format_error/1将三元素元组转换为错误的英文描述。
文件名的编码可以通过epp(3)中描述的注释来设置。
path_eval(Path, Filename) -> {ok, FullName} | {error, Reason}
Types:
Path = [Dir :: name_all()]
Filename = name_all()
FullName = filename_all()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
搜索路径Path(目录名称列表),直到找到文件Filename。如果文件名是绝对文件名,则路径被忽略。然后读取并运算由'.'分隔的Erlang表达式。(或',',表达式序列也是一个表达式)。运算的实际结果不返回;文件中的任何表达式序列都必须存在,因为它的副作用。返回以下内容之一:
{ok,FullName}
该文件被读取和运算。FullName是文件的全名。
{error,enoent}
该文件无法在Path中的任何目录中找到 。
{error,atom()}
打开文件或读取文件时发生错误。有关典型错误代码的列表,请参阅open/2。
{error,{Line,Mod,Term}}
解释文件中的Erlang表达式时发生错误。使用format_error/1将三元素元组转换为错误的英文描述。
文件名的编码可以通过epp(3)中描述的注释来设置。
path_open(Path, Filename, Modes) -> {ok, IoDevice, FullName} | {error, Reason}
Types:
Path = [Dir :: name_all()]
Filename = name_all()
Modes = [mode()]
IoDevice = io_device()
FullName = filename_all()
Reason = posix() | badarg | system_limit
搜索路径Path(目录名称列表),直到找到文件Filename。如果文件名是绝对文件名,则路径被忽略。然后以Modes确定的模式打开文件。返回以下内容之一:
{ok,IoDevice,FullName}
该文件已在请求的模式下打开。 IoDevice是对文件的引用,FullName是文件的全名。
{错误,enoent}
该文件无法在Path中的任何目录中找到 。
{error,atom()}
该文件无法打开。
path_script(Path, Filename) -> {ok, Value, FullName} | {error, Reason}
Types:
Path = [Dir :: name_all()]
Filename = name_all()
Value = term()
FullName = filename_all()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
搜索路径Path(目录名称列表),直到找到文件Filename。如果文件名是绝对文件名,则路径被忽略。然后读取并运算由'.'分隔的Erlang表达式。(或',',表达式序列也是一个表达式)。返回以下内容之一:
{ok,Value,FullName}
该文件被读取和运算。全名是文件的全名值的最后一个表达式的值。
{error,enoent}
该文件无法在Path中的任何目录中找到 。
{error,atom()}
打开文件或读取文件时发生错误。有关典型错误代码的列表,请参阅open/2。
{error,{Line,Mod,Term}}
解释文件中的Erlang表达式时发生错误。使用format_error/1将三元素元组转换为错误的英文描述。
文件名的编码可以通过epp(3)中描述的注释来设置。
path_script(Path, Filename) -> {ok, Value, FullName} | {error, Reason}
Types:
Path = [Dir :: name_all()]
Filename = name_all()
Value = term()
FullName = filename_all()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
搜索路径Path(目录名称列表),直到找到文件Filename。如果文件名是绝对文件名,则路径被忽略。然后读取并运算由'.'分隔的Erlang表达式。(或',',表达式序列也是一个表达式)。返回以下内容之一:
{ok,Value,FullName}
该文件被读取和运算。全名是文件的全名值的最后一个表达式的值。
{error,enoent}
该文件无法在Path中的任何目录中找到 。
{error,atom()}
打开文件或读取文件时发生错误。有关典型错误代码的列表,请参阅open / 2。
{error,{Line,Mod,Term}}
解释文件中的Erlang表达式时发生错误。使用format_error/1将三元素元组转换为错误的英文描述。
文件名的编码可以通过epp(3)中描述的注释来设置。
path_script(Path, Filename, Bindings) -> {ok, Value, FullName} | {error, Reason}
Types:
Path = [Dir :: name_all()]
Filename = name_all()
Bindings = erl_eval:binding_struct()
Value = term()
FullName = filename_all()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
与path_script/2相同,但在运算中使用变量绑定Bindings。请参阅 erl_eval(3)关于变量绑定。
pid2name(Pid) -> {ok, Filename} | undefined
Types:
Filename = filename_all()
Pid = pid()
如果Pid是IO设备,即从open/2返回的pid,则此函数返回文件名,或者更确切地说:
{ok,Filename}
如果此节点的文件服务器不是从属节点,则该节点的文件服务器将打开文件(这意味着 Pid必须是本地pid),并且该文件未关闭。文件名是字符串格式的文件名。
undefined
在所有其他情况下。
警告
该功能仅用于调试。
position(IoDevice, Location) -> {ok, NewPosition} | {error, Reason}
Types:
IoDevice = io_device()
Location = location()
NewPosition = integer()
Reason = posix() | badarg | terminated
将IoDevice引用的文件的位置设置为Location。如果成功,则返回 {ok,NewPosition}(作为绝对偏移量),否则返回 {error,Reason}。Location是以下之一:
Offset
与{bof,Offset}相同。
{bof,Offset}
绝对偏移量。
{cur,Offset}
从当前位置偏移。
{eof,Offset}
从文件末尾偏移。
bof | cur | EOF
与Offset 0 相同。
请注意,偏移量以字节计,而不是字符。如果使用除latin1之外的其他编码打开文件,则一个字节不对应一个字符。在这样的文件中定位只能通过已知的字符边界完成,也就是说,通过获取当前位置,到文件的开始/结尾或其他某些已知位于正确字符边界的位置(通常超出文件中的字节顺序标记,它具有已知的字节大小)。
典型的错误原因是:
einval
无论是位置是非法的,或在文件中其偏移量计算为负。请注意,如果结果位置为负值,则结果为错误,并且在调用后文件位置未定义。
pread(IoDevice, LocNums) -> {ok, DataL} | eof | {error, Reason}
Types:
IoDevice = io_device()
LocNums = [{Location :: location(), Number :: integer() >= 0}]
DataL = [Data]
Data = string() | binary() | eof
Reason = posix() | badarg | terminated
在一次操作中执行pread/3的序列,这比一次调用它们更有效。返回{ok,[Data,...]}或 {error,Reason},其中每个Data(相应的pread的结果 )可以是列表或二进制文件,具体取决于文件的模式,或者eof,如果请求位置超出了文件结尾。
由于位置是以字节偏移量给出的,因此在处理编码设置为latin1以外的文件时必须特别小心,因为并非每个字节位置都是此类文件上的有效字符边界。
pread(IoDevice, Location, Number) -> {ok, Data} | eof | {error, Reason}
Types:
IoDevice = io_device()
Location = location()
Number = integer() >= 0
Data = string() | binary()
Reason = posix() | badarg | terminated
在一次操作中合并position/2和read/2,这比一次调用它们更有效。如果IoDevice已在原始模式下打开,则会有一些限制:位置只允许为整数; 操作后,文件的当前位置未定义。
由于位置是以字节偏移量给出的,因此在处理编码设置为latin1以外的文件时必须特别小心,因为并非每个字节位置都是此类文件上的有效字符边界。
pwrite(IoDevice, LocBytes) -> ok | {error, {N, Reason}}
Types:
IoDevice = io_device()
LocBytes = [{Location :: location(), Bytes :: iodata()}]
N = integer() >= 0
Reason = posix() | badarg | terminated
在一次操作中执行一系列pwrite/3,这比一次调用一个更有效。返回ok或{error,{N,Reason}},其中 N是在失败之前完成的成功写入次数。
当使用除latin1之外的其他编码定位文件时,必须注意将位置设置在正确的字符边界上,详情请参阅position/2。
read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason}
Types:
IoDevice = io_device() | atom()
Number = integer() >= 0
Data = string() | binary()
Reason = posix()
| badarg
| terminated
| {no_translation, unicode, latin1}
从IoDevice引用的文件读取Number字节/字符。read/2,pread/3 和read_line/1函数是从原始模式打开的文件读取的唯一方法(尽管它们也适用于通常打开的文件)。
对于编码设置为latin1以外的文件,文件中的一个字符可能由多个字节表示。参数Number始终表示从文件中读取的字符数,为什么在读取Unicode文件时文件中的位置可能比此数字移动得多。
另外,如果编码设置为latin1以外的其他值,如果数据包含大于255的字符,则read/3调用将失败,为什么读取此类文件时首选io(3)模块。
该函数返回:
{ok,Data}
如果文件以二进制模式打开,读取的字节以二进制形式返回,否则以列表形式返回。如果文件结尾已达到,列表或二进制文件将短于请求的字节数。
eof
如果Number> 0和文件结尾已达到,则返回任何可以读取的内容。
典型的错误原因:
ebadf
该文件未打开以供阅读。
{no_translation,unicode,latin1}
该文件使用另一种编码而不是latin1打开,并且文件中的数据不能转换为该函数返回的字节数据。
read_file(Filename) -> {ok, Binary} | {error, Reason}
Types:
Filename = name_all()
Binary = binary()
Reason = posix() | badarg | terminated | system_limit
返回{ok,Binary},其中Binary是包含Filename的内容的二进制数据对象,或 {error,Reason}如果发生错误。
典型的错误原因:
enoent
该文件不存在。
eacces
缺少的权限读取文件或者搜索其中一个父目录。
eisdir
指定的文件是一个目录。
enotdir
文件名的一个组件不是一个目录。在某些平台上,取而代之的是返回enoent。
enomem
文件内容没有足够的内存。
read_file_info(Filename) -> {ok, FileInfo} | {error, Reason}
read_file_info(Filename, Opts) -> {ok, FileInfo} | {error, Reason}
Types:
Filename = name_all()
Opts = [file_info_option()]
FileInfo = file_info()
Reason = posix() | badarg
检索有关文件的信息。如果成功则返回 {ok,FileInfo},否则返回 {error,Reason}。FileInfo 是一个记录 file_info,在内核包含文件file.hrl中定义 。在调用函数的模块中包含以下指令:
-include_lib("kernel/include/file.hrl").
atime,mtime和ctime中返回的时间类型 取决于Opts::{time,Type}中设置的时间类型。类型local将返回本地时间,universal将返回通用时间,posix将返回自unix time epoch(1970-01-01 00:00 UTC)之前或之后的秒数。默认是{time,local}。
注意
由于文件时间在大多数操作系统上以posix时间存储,因此使用posix选项查询文件信息会更快。
记录file_info包含以下字段。
size = integer()> = 0
文件大小(以字节为单位)
type = device | directory | other | regular | symlink
文件的类型。
access = read | write | read_write | none
当前系统访问该文件。
atime = date_time() | integer()> = 0
上次读取文件时。
mtime = date_time() | integer()> = 0
上次写入文件的时间。
ctime = date_time() | integer()> = 0
这段时间的解释取决于操作系统。在Unix上,它是最后一次更改文件或inode。在Windows中,这是创建时间。
mode = integer() >= 0
文件权限为以下位值的总和:
8#00400
读取权限:所有者
8#00200
写权限:所有者
8#00100
执行权限:所有者
8#00040
读权限:组
8#00020
写入权限:组
8#00010
执行权限:组
8#00004
读权限:其他
8#00002
写入权限:其他
8#00001
执行权限:其他
16#800
在执行时设置用户ID
16#400
执行时设置组ID
在Unix平台上,可以设置除上面列出的位之外的其他位。
links = integer()> = 0
文件链接的数量(对于没有链接概念的文件系统,这总是1)。
major_device = integer()> = 0
标识文件所在的文件系统。在Windows中,数字表示一个驱动器,如下所示:0表示A :, 1表示B :,依此类推。
minor_device = integer()> = 0
只对Unix上的字符设备有效。在所有其他情况下,该字段为零。
inode = integer()> = 0
给出inode编号。在非Unix文件系统上,此字段将为零。
uid = integer()> = 0
表示文件的所有者。非Unix文件系统将为零。
gid = integer()> = 0
给出文件所有者属于的组。对于非Unix文件系统将为零。
典型的错误原因:
eacces
缺少文件父目录之一的搜索权限。
enoent
该文件不存在。
enotdir
文件名的一个组件不是一个目录。在某些平台上,取而代之的是返回enoent。
read_line(IoDevice) -> {ok, Data} | eof | {error, Reason}
Types:
IoDevice = io_device() | atom()
Data = string() | binary()
Reason = posix()
| badarg
| terminated
| {no_translation, unicode, latin1}
从IoDevice引用的文件中读取一行字节/字符 。行被定义为由换行(LF,\n)字符分隔,但任何回车符(CR,\r)后跟换行符也被视为单个LF字符(回车被忽略)。该行返回包括LF,但不包括任何紧跟着LF的CR。此行为与io:get_line/2的行为一致。如果在没有任何LF结束最后一行的情况下到达文件末尾,则返回没有尾随LF的行。
该功能可用于以原始模式打开的文件。但是,如果未使用指定的{read_ahead,Size}选项打开文件,则在原始文件上使用它是低效的,这就是为什么在打开面向原始行读取的文本文件时强烈建议组合raw和{read_ahead,Size}。
如果编码设置为latin1以外的其他值,如果数据包含大于255的字符,则read_line/1调用将失败,为什么读取此类文件时首选io(3)模块。
该函数返回:
{ok,Data}
返回文件中的一行,包括尾随LF,但CRLF序列由单个LF替换(参见上文)。
如果文件以二进制模式打开,读取的字节以二进制形式返回,否则以列表形式返回。
eof
如果在读取任何内容之前已达到文件结尾,则返回。
{error, Reason}
发生错误。
典型的错误原因:
ebadf
该文件未打开以供阅读。
{no_translation,unicode,latin1}
该文件是使用除latin1之外的其他编码打开的,并且该文件上的数据无法转换为此函数返回的面向字节的数据。
read_link(Name) -> {ok, Filename} | {error, Reason}
Types:
Name = name_all()
Filename = filename()
Reason = posix() | badarg
如果 Name引用不是“原始”文件名的符号链接,则返回{ok,Filename},否则返回{error,Reason} 。在不支持符号链接的平台上,返回值将是{error,enotsup}。
典型的错误原因:
einval
名称不引用符号链接或引用的文件的名称不符合预期的编码。
enoent
该文件不存在。
enotsup
此平台不支持符号链接。
read_link_all(Name) -> {ok, Filename} | {error, Reason}
Types:
Name = name_all()
Filename = filename_all()
Reason = posix() | badarg
如果Name引用符号链接 ,则返回{ok,Filename},否则 返回{ error,Reason}。在不支持符号链接的平台上,返回值将是{error,enotsup}。
请注意,文件名可以是列表或二进制文件。
典型的错误原因:
einval
名称不是指符号链接。
enoent
该文件不存在。
enotsup
此平台不支持符号链接。
read_link_info(Name) -> {ok, FileInfo} | {error, Reason}
read_link_info(Name, Opts) -> {ok, FileInfo} | {error, Reason}
Types:
Name = name_all()
Opts = [file_info_option()]
FileInfo = file_info()
Reason = posix() | badarg
这个函数的作用类似于read_file_info/1,2,除了如果Name是一个符号链接,关于该链接的信息将在file_info记录中返回并且该记录的类型字段将被设置为 符号链接。
如果Name不是符号链接,则此函数返回与read_file_info/1完全相同的结果。在不支持符号链接的平台上,此函数始终等效于read_file_info/1。
rename(Source, Destination) -> ok | {error, Reason}
Types:
Source = Destination = name_all()
Reason = posix() | badarg
尝试将文件Source重命名为Destination。它可用于在目录之间移动文件(和目录),但仅指定目标是不够的。目标文件名也必须指定。例如,如果bar是普通文件,而foo和baz是目录,则重命名(“foo / bar”,“baz”)会返回错误,但重命名(“foo / bar”,“baz / bar”)会成功。如果成功则返回ok。
注意
在大多数平台上不允许重命名打开的文件(请参阅下面的eacces)。
典型的错误原因:
eacces
缺少源或目标的父目录的读或写权限。在某些平台上,如果Source或Destination处于打开状态,则会出现此错误 。
eexist
目的地不是空目录。在某些平台上,当源和目标不是同一类型时也会给出。
einval
Source是根目录,或者Destination是Source的子目录。
eisdir
目的地是一个目录,但Source不是。
enoent
来源不存在。
enotdir
Source是一个目录,但Destination不是。
exdev
源和目标位于不同的文件系统上。
script(Filename) -> {ok, Value} | {error, Reason}
Types:
Filename = name_all()
Value = term()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
读取和运算由'.'分隔的Erlang表达式。(或',',表达式序列也是表达式),来自文件。返回以下之一:
{ok,Value}
该文件被读取和运算。值是最后一个表达式的值。
{error,atom()}
打开文件或读取文件时发生错误。有关典型错误代码的列表,请参见open/2。
{error,{Line,Mod,Term}}
解释文件中的Erlang表达式时发生错误。使用format_error/1将三元素元组转换为错误的英文描述。
文件名的编码可以通过epp(3)中描述的注释来设置。
script(Filename, Bindings) -> {ok, Value} | {error, Reason}
Types:
Filename = name_all()
Bindings = erl_eval:binding_struct()
Value = term()
Reason = posix()
| badarg
| terminated
| system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}
与script/1相同,但在运算中使用变量绑定Bindings。请参阅有关变量绑定的erl_eval(3)。
set_cwd(Dir) -> ok | {error, Reason}
Types:
Dir = name()
Reason = posix() | badarg | no_translation
将文件服务器的当前工作目录设置为Dir。如果成功则返回ok。
典型的错误原因是:
enoent
该目录不存在。
enotdir
Dir的一个组件不是一个目录。在某些平台上,返回enoent。
eacces
没有目录或其父目录的权限。
badarg
Dir有一个不正确的类型,比如元组。
no_translation
Dir是一个二进制,其字符以ISO-latin-1编码,VM以参数+ fnue开始。
警告
在未来的版本中,Dir 参数的错误类型可能会生成异常。
sync(IoDevice) -> ok | {error, Reason}
Types:
IoDevice = io_device()
Reason = posix() | badarg | terminated
确保操作系统(而不是Erlang运行时系统)保存的任何缓冲区都写入磁盘。在某些平台上,此功能可能不起作用。
典型的错误原因是:
enospc
没有足够的空间来写入文件。
datasync(IoDevice) -> ok | {error, Reason}
Types:
IoDevice = io_device()
Reason = posix() | badarg | terminated
确保操作系统(而不是Erlang运行时系统)保存的任何缓冲区都写入磁盘。在许多方面,它类似于fsync,但不需要更新文件的某些元数据,如访问时间。在某些平台上,此功能可能不起作用。
访问数据库或日志文件的应用程序通常会写入一个小小的数据片段(例如,日志文件中的一行),然后立即调用fsync()以确保写入的数据物理存储在硬盘上。不幸的是,fsync()将始终启动两个写操作:一个用于新写入的数据,另一个用于更新存储在inode中的修改时间。如果修改时间不是事务概念的一部分,则可以使用fdatasync()来避免不必要的inode磁盘写入操作。
仅在某些POSIX系统中可用。此调用会导致在未实现fdatasync系统调用的系统中调用fsync()或无效。
truncate(IoDevice) -> ok | {error, Reason}
Types:
IoDevice = io_device()
Reason = posix() | badarg | terminated
截断在当前位置由IoDevice引用的文件。如果成功则返回ok,否则{error,Reason}。
sendfile(Filename, Socket) -> {ok, integer() >= 0} | {error, inet:posix() | closed | badarg | not_owner}
Types:
Filename = name_all()
Socket = inet:socket()
将文件Filename发送到Socket。如果成功则返回{ok,BytesSent},否则返回{error,Reason}。
sendfile(RawFile, Socket, Offset, Bytes, Opts) ->
{ok, integer() >= 0} |
{error, inet:posix() | closed | badarg | not_owner}
Types:
RawFile = fd()
Socket = inet:socket()
Offset = Bytes = integer() >= 0
Opts = [sendfile_option()]
sendfile_option() = {chunk_size, integer() >= 0}
从偏移量开始,从RawFile引用的文件发送字节到套接字。如果成功则返回{ok,BytesSent},否则返回{error,Reason}。如果字节设置为0,则发送给定偏移后的所有数据。
必须使用原始标志打开使用的文件,并且调用sendfile的进程必须是套接字的控制进程。请参见gen_tcp:controlling_process/2
如果使用的操作系统不支持sendfile,则使用file:read和gen_tcp:send的Erlang fallback。
选项列表可以包含以下选项:
chunk_size
erlang fallback用于发送数据的块大小。如果使用fallback功能,则应将其设置为适合系统内存的值。默认值是20 MB。
在具有线程支持的操作系统上,建议使用异步线程。看命令行标记 +A在ERL(1) 。如果无法使用sendfile的异步线程,则建议为套接字上的发送缓冲区使用相对较小的值。否则,Erlang虚拟机可能会失去一些软实时保证。要使用哪种大小取决于操作系统/硬件和应用程序的要求。
write(IoDevice, Bytes) -> ok | {error, Reason}
Types:
IoDevice = io_device() | atom()
Bytes = iodata()
Reason = posix() | badarg | terminated
将字节写入IoDevice引用的文件 。此功能是写入以原始模式打开的文件的唯一方式(尽管它也适用于通常打开的文件)。如果成功则返回ok ,否则返回{error,Reason}。
如果使用设置为latin1以外的编码打开文件,写入的每个字节都可能导致实际写入文件的几个字节,因为字节范围0..255可能表示1到4个字节之间的任何值,具体取决于值和UTF编码类型。
典型的错误原因是:
ebadf
该文件未打开以进行写入。
enospc
设备上没有剩余空间。
write_file(Filename, Bytes) -> ok | {error, Reason}
Types:
Filename = name_all()
Bytes = iodata()
Reason = posix() | badarg | terminated | system_limit
将iodata的内容写入文件Filename。如果文件不存在,则创建该文件。如果存在,则先前的内容被覆盖。返回ok,或者{error,Reason}。
典型的错误原因是:
enoent
文件名的一个组件不存在。
enodir
文件名的一个组件不是一个目录。在某些平台上,取而代之的是返回enoent。
enospc
设备上没有剩余空间。
eacces
缺少写入文件或搜索其中一个父目录的权限。
eisdir
指定的文件是一个目录。
write_file(Filename, Bytes, Modes) -> ok | {error, Reason}
Types:
Filename = name_all()
Bytes = iodata()
Modes = [mode()]
Reason = posix() | badarg | terminated | system_limit
与write_file/2相同,但采用第三个参数Modes,一个可能模式的列表,请参阅open/2。模式标志binary和write是隐含的,所以它们不应该被使用。
write_file_info(Filename, FileInfo) -> ok | {error, Reason}
write_file_info(Filename, FileInfo, Opts) -> ok | {error, Reason}
更改文件信息。如果成功则返回ok,否则{error,Reason}。 FileInfo是一个记录 file_info,在内核包含文件file.hrl中定义 。在调用该函数的模块中包含以下指令:
-include_lib("kernel/include/file.hrl").
在设定的时间类型atime,mtime和ctime 依赖于设定的时间类型Opts::{time,Type}}。类型local将解释时间设置为本地,universal将解释为通用时间,posix必须是自1970年1月1日00:00 UTC之前或之前的秒数。默认是{time,local}。
如果给出了以下字段,则从记录中使用。
atime = date_time() | integer()> = 0
上次读取文件时。
mtime = date_time() | integer()> = 0
上次写入文件的时间。
ctime = date_time() | integer()> = 0
在Unix上,该字段的任何值将被忽略(文件的“ctime”将被设置为当前时间)。在Windows上,此字段是为文件设置的新创建时间。
mode = integer()> = 0
文件权限为以下位值的总和:
8#00400
读取权限:所有者
8#00200
写权限:所有者
8#00100
执行权限:所有者
8#00040
读权限:组
8#00020
写入权限:组
8#00010
执行权限:组
8#00004
读权限:其他
8#00002
写入权限:其他
8#00001
执行权限:其他
16#800
在执行时设置用户ID
16#400
执行时设置组ID
在Unix平台上,可以设置除上面列出的位之外的其他位。
uid = integer()> = 0
表示文件的所有者。忽略非Unix文件系统。
gid = integer()> = 0
给出文件所有者属于的组。忽略非Unix文件系统。
典型的错误原因:
eacces
缺少文件父目录之一的搜索权限。
enoent
该文件不存在。
enotdir
文件名的一个组件不是一个目录。在某些平台上,取而代之的是返回enoent。
POSIX错误代码
eacces - 权限被拒绝
eagain - 资源暂时不可用
ebadf - 错误的文件编号
ebusy - 文件忙
edquot - 超过磁盘配额
eexist - 文件已经存在
efault - 系统调用参数中的错误地址
efbig - 文件太大
eintr - 中断系统调用
einval - 无效的参数
eio - IO错误
eisdir - 目录上的非法操作
eloop - 太多级别的符号链接
emfile - 太多打开的文件
emlink - 太多的链接
enametoolong - 文件名太长
enfile - 文件表溢出
enodev - 没有这样的设备
enoent - 没有这样的文件或目录
enomem - 内存不够
enospc - 设备上没有剩余空间
enotblk - 需要块设备
enotdir - 不是目录
enotsup - 不支持操作
enxio - 没有这样的设备或地址
eperm - 不是所有者
epipe - 破的管道
erofs - 只读文件系统
espipe - 无效搜寻
esrch - 没有这样的进程
estale - 陈旧的远程文件句柄
exdev - 跨域链接
性能
某些操作系统文件操作(例如,大文件上的sync/1或close/1)可能会阻止其调用线程几秒钟。如果这种情况出现在仿真器主线程中,则响应时间不再是毫秒数量级,这取决于软实时系统中“软”的定义。
如果设备驱动程序线程池处于活动状态,则文件操作将通过这些线程完成,以便模拟器可以继续执行Erlang进程。不幸的是,由于操作系统需要额外的调度,服务文件操作的时间增加了。
如果设备驱动程序线程池被禁用或大小为0,则大文件读取和写入会被分割为几个较小的文件,这使得模拟器可以在文件操作期间为其他进程提供服务。这与使用线程池时的效果相同,但开销较大。其他文件操作(例如,大文件上的sync/1或close/1)仍然存在问题。
为了提高性能,建议使用原始文件。原始文件使用节点主机的文件系统。对于普通文件(非原始文件),文件服务器用于查找文件,如果节点正在将其文件服务器作为从属节点运行到其他节点,并且其他节点在其他主机上运行,则它们可能具有不同的文件系统。这很少是一个问题,但你现在已经受到警告。
普通文件实际上是一个进程,因此它可以用作IO设备(请参阅io)。因此,当数据写入普通文件时,将数据发送到文件进程将复制所有非二进制数据。因此建议以二进制模式打开文件并编写二进制文件。如果文件在另一个节点上打开,或者文件服务器作为另一个节点的从服务器运行,则也会复制二进制文件。
缓存数据以减少文件操作的数量,或者说调用文件驱动程序的次数通常会提高性能。以下函数在测试时在23秒内写入4 MBytes:
create_file_slow(Name, N) when integer(N), N >= 0 ->
{ok, FD} = file:open(Name, [raw, write, delayed_write, binary]),
ok = create_file_slow(FD, 0, N),
ok = ?FILE_MODULE:close(FD),
ok.
create_file_slow(FD, M, M) ->
ok;
create_file_slow(FD, M, N) ->
ok = file:write(FD, <<M:32/unsigned>>),
create_file_slow(FD, M+1, N).
在每次调用file:write/2之前,以下功能相当的函数将1024个条目收集到128个32字节二进制文件列表中, 并在0.52秒内完成相同的工作,速度提高了44倍。
create_file(Name, N) when integer(N), N >= 0 ->
{ok, FD} = file:open(Name, [raw, write, delayed_write, binary]),
ok = create_file(FD, 0, N),
ok = ?FILE_MODULE:close(FD),
ok.
create_file(FD, M, M) ->
ok;
create_file(FD, M, N) when M + 1024 =< N ->
create_file(FD, M, M + 1024, []),
create_file(FD, M + 1024, N);
create_file(FD, M, N) ->
create_file(FD, M, N, []).
create_file(FD, M, M, R) ->
ok = file:write(FD, R);
create_file(FD, M, N0, R) when M + 8 =< N0 ->
N1 = N0-1, N2 = N0-2, N3 = N0-3, N4 = N0-4,
N5 = N0-5, N6 = N0-6, N7 = N0-7, N8 = N0-8,
create_file(FD, M, N8,
[<<N8:32/unsigned, N7:32/unsigned,
N6:32/unsigned, N5:32/unsigned,
N4:32/unsigned, N3:32/unsigned,
N2:32/unsigned, N1:32/unsigned>> | R]);
create_file(FD, M, N0, R) ->
N1 = N0-1,
create_file(FD, M, N1, [<<N1:32/unsigned>> | R]).
注意
只相信你自己的基准。如果上面的create_file/2中的列表长度增加了,它将运行得稍微快一点,但会消耗更多的内存并导致更多的内存碎片。这对您的应用程序有多大影响是这个简单的基准测试无法预测的。
如果每个二进制文件的大小增加到64字节,它也会运行得稍微快一些,但代码会笨拙一倍。在当前的实现中,大于64字节的二进制文件存储在所有进程共有的内存中,并且在进程之间发送时不复制,而这些较小的二进制文件存储在进程堆中,并在发送时像其他任何字段一样进行复制。
因此,对于68字节的二进制大小,create_file/2的运行速度比64字节慢30%,并且会导致更多的内存碎片。请注意,如果要在进程之间发送二进制文件(例如非原始文件),结果可能会完全不同。
原始文件实际上是一个端口。将数据写入端口时,编写二进制文件列表非常有效。在写之前,没有必要将深度列表弄平。在Unix主机上,尽可能使用分散输出(在一个操作中写入一组缓冲区)。通过这种方式file:write(FD,[Bin1,Bin2 | Bin3])将写入二进制文件的内容,而不会复制数据,除了可能在操作系统内核深处。
对于原始文件,pwrite/2和pread/2被高效地实现。文件驱动程序只对整个操作调用一次,并且列表迭代在文件驱动程序中完成。
选项delayed_write和read_ahead到 file:open/2使文件驱动程序缓存数据减少操作系统调用的数量。上面示例中的函数 create_file/2需要60秒,而不使用delayed_write选项,速度较慢2.6倍。
而且,作为一个非常糟糕的例子,create_file_slow/2上面没有raw,binary和delayed_write选项,也就是它调用file:open(Name,[write]),这个作业需要1分20秒,这比慢3.5倍比第一个例子慢150倍,比优化的create_file/2慢150倍。