fopen ()- 打开文件

格式

#include <stdio.h>
FILE *fopen(const char *filename, const char *mode);

语言级别

ANSI

线程安全

描述

fopen() 函数将打开由 filename指定的文件。 mode 参数是一个字符串,用于指定为文件请求的访问类型。 mode 变量包含一个后跟可选关键字参数的位置参数。

注: 当程序使用 SYSIFCOPT (*IFSIO) 或 SYSIFCOPT (*IFS64IO) 进行编译,并且 fopen() 在集成文件系统中创建文件时,将为该文件的所有者,所有者组和公众授予对该文件的读,写和执行权限。
位置参数的可能值为:
方式
描述
阶梯
打开文本文件进行读取。 该文件必须存在。
w
创建要写入的文本文件。 如果给定文件存在,那么其内容将被销毁,除非它是逻辑文件。
软件
以附加方式打开文本文件,以便在文件末尾进行写入。 如果文件不存在并且不是逻辑文件,那么 fopen() 函数会创建该文件。
+
打开文本文件进行读写操作。 该文件必须存在。
w +
创建用于读写的文本文件。 如果给定文件存在,那么将清除其内容,除非它是逻辑文件。
a +
以追加方式打开文本文件,以便在文件末尾进行读取或更新。 如果文件不存在,那么 fopen() 函数会创建该文件。
RB
打开二进制文件以进行读取。 该文件必须存在。
wb
创建用于写入的空二进制文件。 如果该文件存在,那么将清除其内容,除非它是逻辑文件。
ab
以追加方式打开二进制文件,以便在文件末尾进行写入。 如果该文件不存在,那么 fopen 函数将创建该文件。
r + b rb +
打开二进制文件以进行读取和写入。 该文件必须存在。
w + b wb +
创建用于读写的空二进制文件。 如果该文件存在,那么将清除其内容,除非它是逻辑文件。
a + b ab +
以追加方式打开二进制文件,以便在文件末尾进行写入。 如果文件不存在,那么 fopen() 函数会创建该文件。
注:
  1. 使用属性 type=recordab + , rb + ,wb + 打开的文件不支持 fopen() 函数
  2. 小心地使用 ww +wbw + bwb + 参数; 同名现有文件中的数据将丢失。
文本文件 包含组织成行的可打印字符和控制字符。 每行都以换行符结尾,但可能是最后一行,这取决于编译器。 系统可以在输出文本流中插入或转换控制字符。 fopen() 函数方式 "a" 和 "a +" 不能用于 QSYS.LIB 文件系统。 使用 QSYS.LIB 文件系统。 无法依靠在文件启动之后进行查找来处理以文本方式打开的流。
注: 当您使用 fopen() 在 QSYS.LIB 文件系统导致在 QTEMP 库中创建文件。

如果文本文件不存在,那么可以使用以下命令创建一个文本文件:

CRTSRCPF FILE(MYLIB/MYFILE) RCDLEN(LRECL) MBR(MYMBR) SYSTEM(*FILETYPE)

注: 文本流的数据输出可能无法与输入中的相同数据进行比较。 QSYS.LIB 文件系统将数据库文件视为成员目录。 使用 fopen() 函数时,必须先存在数据库文件,然后才能动态创建成员。

请参阅 "信息中心" 中 "集成文件系统" 主题中的 大型文件支持 ,以了解集成文件系统的当前文件系统限制。 对于集成文件系统中大于 2 GB 的文件,您需要允许应用程序访问 64 位 C 运行时函数。 您可以使用以下方法来允许程序访问:

  • 在编译命令上指定 SYSIFCOPT (*IFS64IO) ,这将导致本机 C 编译器定义 _IFS64_IO_。 这将导致定义宏 _LARGE_FILES 和 _LARGE_FILE_API。
  • 在程序源中或通过在编译命令上指定 DEFINE ('_LARGE_FILES') 来定义宏 _LARGE_FILES。 现有的 C 运行时函数和代码中的相关数据类型都将自动映射或重新定义到它们的 64 位版本。
  • 在程序源中或通过在编译命令上指定 DEFINE ('_LARGE_FILE_API') 来定义宏 _LARGE_FILE_API。 这使新的 64 位 C 运行时函数和数据类型的集合可见。 应用程序必须显式指定要使用的 C 运行时函数的名称 (现有版本和 64 位版本)。

64 位 C 运行时函数包括以下内容: int fgetpos64()FILE *fopen64()FILE *freopen64()FILE *wfopen64()int fsetpos64(FILE *, const fpost64_t *)FILE *tmpfile64()int fseeko(FILE *, off_t, int)int fseeko64(FILE *, off64_t, int)off_t ftello(FILE *)off64_t ftello64()

二进制文件 包含一系列字符。 对于二进制文件,系统不会转换输入或输出上的控制字符。

如果二进制文件不存在,那么可以使用以下命令创建一个二进制文件:

CRTPF FILE(MYLIB/MYFILE) RCDLEN(LRECL) MBR(MYMBR) MAXMBRS(*NOMAX) SYSTEM(*FILETYPE)

aa +aba + bab + 方式打开文件时,所有写操作都在文件末尾进行。 虽然您可以使用 fseek() 函数或 rewind() 函数来重新定位文件指针,但是写函数会将文件指针移回文件末尾,然后再执行任何操作。 此操作将阻止您覆盖现有数据。

当您指定更新方式 (在第二个或第三个位置使用 + ) 时,既可以读取文件,也可以写入文件。 但是,在读写之间切换时,必须包含中间定位函数,例如 fseek()fsetpos()rewind()fflush()。 如果检测到文件结束,那么输出可以紧跟在输入之后。

非Integrated File System 的关键字参数

blksize=
指定物理记录块的最大长度 (以字节计)。
lrecl=
指定固定长度记录的长度 (以字节计) 和可变长度记录的最大长度。
recfm=
value 可以是:
F
固定长度,已解锁的记录
FB
固定长度,分块记录
V
可变长度,已解锁的记录
VB
可变长度,分块记录
VBS
磁带文件的可变长度,分块,跨段记录
VS
磁带文件的变长,反锁,跨越记录
磁带文件的 ASCII D 格式的可变长度,已解锁,未跨越的记录
DB
用于磁带文件的 ASCII D 格式的可变长度,分块,未跨越的记录
U
磁带文件的未定义格式
FA
对打印机文件使用第一个字符格式控制数据的固定长度
注: 如果文件是使用 CTLCHAR (*FCFC) 创建的,那么将使用第一个字符格式控制。 如果它是使用 CTLCHAR (*NONE) 创建的,那么将不使用第一个字符格式控制。
commit=
value 可以是:

N 此参数标识此文件未在落实控制下打开。 这是缺省情况。

Y 此参数标识此文件是在落实控制下打开的。

ccsid=
如果指定了操作系统不支持的 CCSID ,那么数据管理将忽略该 CCSID。

当在编译命令上指定 LOCALETYPE (*LOCALEUTF) 时,缺省值为 LC_CTYPE CCSID 值,该值由当前语言环境设置确定。 请参阅 setlocale ()-Set Locale 以获取有关语言环境设置的更多信息。 当未在编译命令上指定 LOCALETYPE (*LOCALEUTF) 时,缺省值为作业 CCSID 值。 有关文件 CCSID 值的更多信息,请参阅 文件 CCSID

arrseq=
value 可以是:

N 此参数标识以创建此文件的方式处理此文件。 这是缺省情况。

Y 此参数标识此文件按到达顺序进行处理。

指示符 =
value 可以是:

N 此参数标识显示, ICF 或打印机文件中的指示符存储在文件缓冲区中。 这是缺省情况。

Y 此参数标识显示, ICF 或打印机文件中的指示符存储在单独的指示符区域中,而不是存储在文件缓冲区中。 文件缓冲区是系统在写入和读取时用于向用户程序和操作系统传输数据的区域。 处理 ICF 文件时,必须将指示符存储在单独的指示符区域中。

类型 =
value 可以是:

memory 此参数将此文件标识为仅可从 C 程序获取的内存文件。 这是缺省情况。

record 此参数指定要为顺序记录 I/O 打开文件。 必须将该文件作为二进制文件打开; 否则, fopen() 函数将失败。 读写操作是使用 fread() 函数和 fwrite() 函数完成的。

仅适用于 Integrated File System 的关键字参数

类型 =
value 可以是:

记录 为顺序记录 I/O 打开文件。 (文件必须作为二进制流打开。)

ccsid=
ccsid 将转换为代码页值。 缺省值是使用作业 CCSID 值作为代码页。 不能同时指定 CCSID 和代码页选项。 CCSID 选项提供与操作系统和基于数据管理的流 I/O 的兼容性。
注: 对于文本的文件数据处理方式,不支持混合数据 (数据同时包含单字节和双字节字符)。 对于二进制文件处理方式,支持混合数据。

如果指定 ccsid 关键字,那么不能指定 o_ccsid 关键字或代码页关键字。

由于可能扩展或收缩转换后的数据,因此对数据大小和当前文件偏移量进行假设是危险的。 例如,文件的物理大小可能为 100 字节,但应用程序从文件中读取 100 字节后,当前文件偏移量可能仅为 50。 为了读取整个文件,应用程序可能必须读取 200 字节或更多字节,具体取决于所涉及的 CCSID。 因此,文件定位功能 (例如 ftell()fseek()fgetpos()fsetpos()) 可能不起作用。 这些函数可能因错误 ENOTSUP 而失败。 如果开启了缓冲,那么读取函数也将不起作用,因为缺省情况下是这样。 要关闭缓冲,请将 setvbuf 函数与 _IONBF 关键字配合使用。

发生以下所有三种情况时, fopen() 函数可能会失败,并返回 ECONVERT 错误:
  • 文件数据处理方式为文本。
  • 未指定代码页。
  • 作业的 CCSID 是 "mixed-data" (数据同时包含单字节和双字节字符)。
o_ccsid=

当在编译命令上指定 LOCALETYPE (*LOCALEUTF) 时,缺省值为 LC_CTYPE CCSID 值,该值由当前语言环境设置确定。 请参阅 setlocale ()-Set Locale 以获取有关语言环境设置的更多信息。 当未在编译命令上指定 LOCALETYPE (*LOCALEUTF) 时,缺省值为作业 CCSID 值。 有关文件 CCSID 值的更多信息,请参阅 文件 CCSID

此参数类似于 ccsid 参数,只是指定的值未转换为代码页。 此外,还支持混合数据。 如果创建了该文件,那么将使用指定的 CCSID 对其进行标记。 如果该文件已存在,那么将在读操作时将数据从该文件的 CCSID 转换为指定的 CCSID。 在写操作时,假定数据使用指定的 CCSID ,并将其转换为文件的 CCSID。

由于可能扩展或收缩转换后的数据,因此对数据大小和当前文件偏移量进行假设是危险的。 例如,文件的物理大小可能为 100 字节,但应用程序从文件中读取 100 字节后,当前文件偏移量可能仅为 50。 为了读取整个文件,应用程序可能必须读取 200 字节或更多字节,具体取决于所涉及的 CCSID。 因此, ftell()fseek()fgetpos()fsetpos() 之类的文件定位功能将不起作用。 这些函数将因 ENOTSUP 而失败。 如果开启了缓冲,那么读取函数也将不起作用,因为缺省情况下是这样。 要关闭缓冲,请将 setvbuf 函数与 _IONBF 关键字配合使用。

使用 o_ccsid 的示例

/* Create a file that is tagged with CCSID 37 */
if ((fp = fopen("/MYFILE" , "w, o_ccsid=37")) == NULL) {
   printf("Failed to open file with o_ccsid=37\n");
}

fclose(fp);

/* Now reopen the file with CCSID 13488, because your application
 wants to deal with the data in UNICODE */

if ((fp = fopen("/MYFILE" , "r+, o_ccsid=13488")) == NULL) {
   printf("Failed to open file with o_ccsid=13488\n");
}
/* Turn buffering off because read functions do not work when
buffering is on */

if (setbuf(fp, NULL, _IONBF, 0) != 0){
    printf("Unable to turn buffering off\n");
}
/* Because you opened with o_ccsid = 13488, you must provide
all input data as unicode.
If this program is compiled with LOCALETYPE(*LOCALEUCS2),
L constrants will be unicode. */

funcreturn = fputws(L"ABC", fp); /* Write a unicode ABC to the file. */

if (funcreturn < 0) {
   printf("Error with 'fputws' on line %d\n", __LINE__);
}
/* Because the file was tagged with CCSID 37, the unicode ABC was
converted to EBCDIC ABC when it was written to the file. */
codepage=
将使用 value 指定的代码页。

如果指定代码页关键字,那么不能指定 ccsid 关键字或 o_ccsid 关键字。

如果要打开的文件不存在,并且打开方式指定应该创建该文件,那么将创建该文件并使用计算的代码页进行标记。 如果该文件已存在,那么在读操作期间,从该文件读取的数据将从该文件的代码页转换为计算的代码页。 写入文件的数据假定在计算的代码页中,并在写操作期间转换为文件的代码页。

crln=
value 可以是:

Y 要使用的行终止符是回车符 [CR] ,新行 [NL] 组合。 读取数据时,将针对字符串函数剥离所有回车符 [CR]。 将数据写入文件时,将在每个新行 [NL] 字符之前添加回车符 [CR]。 仅当文件以文本方式打开时,才会进行行终止符处理。 这是缺省情况。

N 要使用的行终止符仅为新行 [NL]。

关键字参数不区分大小写,应以逗号分隔。

如果参数不匹配,那么 fopen() 函数通常会失败。

返回值

fopen() 函数返回一个指向 FILE 结构类型的指针,该结构类型可用于访问打开的文件。
注: 要将流文件 (type = record) 与记录 I/O 函数配合使用,必须将 FILE 指针强制转换为 RFILE 指针。

NULL 指针返回值指示错误。

errno 的值可以设置为:
含义
EBADMODE
指定的文件方式无效。
EBADNAME
指定的文件名无效。
经济和
发生转换错误。
ENOENT
无文件或库。
ENOMEM
存储器分配请求失败。
ENOTOPEN
文件未打开。
EIOERROR
发生了不可恢复的I/O错误。
EIORECERR
发生了可恢复的I/O错误。
Escanfailure
该文件被标记为扫描失败。

如果传递到 fopen() 的方式字符串正确,那么无论文件类型如何, fopen() 都不会将 errno 设置为 EBADMODE。

如果传递到 fopen() 的方式字符串无效,那么 fopen() 会将 errno 设置为 EBADMODE ,而不考虑文件类型。

如果传递到 fopen() 的方式字符串正确,但对于该特定类型的文件无效,那么 fopen() 会将 errno 设置为 ENOTOPEN , EIOERROR 或 EIORECERR ,而不考虑文件类型。

示例

此示例尝试打开文件以进行读取。
#include <stdio.h>
#define  MAX_LEN  60
 
int main(void)
{
   FILE *stream;
   fpos_t pos;
   char line1[MAX_LEN];
   char line2[MAX_LEN];
   char *result;
   char ch;
   int num;
 
   /* The following call opens a text file for reading.   */
   if ((stream = fopen("mylib/myfile", "r")) == NULL)
      printf("Could not open data file\n");
   else if ((result = fgets(line1,MAX_LEN,stream)) != NULL)
           {
            printf("The string read from myfile: %s\n", result);
            fclose(stream);
           }
 
   /* The following call opens a fixed record length file */
   /* for reading and writing.                            */
   if ((stream = fopen("mylib/myfile2", "rb+, lrecl=80,  \
                 blksize=240, recfm=f")) == NULL)
         printf("Could not open data file\n");
   else {
         fgetpos(stream, &pos);
         if (!fread(line2,sizeof(line2),1,stream))
            perror("fread error");
         else printf("1st record read from myfile2: %s\n", line2);
 
         fsetpos(stream, &pos);     /* Reset pointer to start of file */
         fputs(result, stream);     /* The line read from myfile is   */
                                    /* written to myfile2.            */
         fclose(stream);
        }
}

相关信息