第四节 文件处理(File handling)

第四节 文件处理(File handling)

When we handle files, we have to call apr_file_open() at first. Here is the prototype declaration.

当我们需要处理文件的时候,首先我们需要调用apr_file_open(),这是他的原型声明:

/* excerpted from apr_file_io.h */

APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **newf, const char *fname, apr_int32_t flag, apr_fileperms_t perm, apr_pool_t *pool);

The first argument type is apr_file_t**, which is result argument. Namely, you can create an apr_file_t object by calling apr_file_open(). The second argument is file name path. The third argument is a bit-wised flag. The bit-flags are defined in apr_file_io.h. The fourth argument is file access permission, which has effects on a newly created file. The value is bit-wised flag. The bit-flags are defined in apr_file_info.h. For example, if you want to create a file that has an access permission 0600, i.e. read-write access only by the file owner, you have to specify APR_UREAD|APR_UWRITE. In usual cases, you can use APR_OS_DEFAULT as file permission. The fifth final argument is a memory pool to use. Needless to say, you need to create the memory pool by apr_pool_create().

第一个参数类型是apr_file_t**,它是一个结构参数(result argument),也就是你能够通过调用apr_file_open()创建一个apr_file_t对象。第二个参数是文件的路径。第三个参数是一个比特方式的标志位,它定义在apr_file_io.h中。第四个参数是文件的访问权限,这个参数在会影响新创建出来的文件,这个值是一个比特方式的标志位,这个标志位定义在apr_file_info.h中。例如:你想创建一个访问权限是0600(0600表示只有文件所有者可以读、写这个文件)的文件,你需要指定这个标志位为APR_UREAD|APR_UWRITE,通常你可以使用APR_OS_DEFAULT,这表示系统的默认权限。第五个参数是内存池,不必要的话,你需要使用apr_pool_create()创建一个内存池。

After we open file, we can handle file by other APIs. We can find them in apr_file_io.h. The basic APIs are apr_file_read() and apr_file_write(). As you expect, apr_file_read() allows us to read something from file, and apr_file_write() allows us to write something to file. Here is the prototype declarations,

当我们打开一个文件之后,我们可以通过其他的API对文件进行处理,我们可以在apr_file_io.h找到他们。基本的API是apr_file_read()和apr_file_write(),正和你预料的一样,apr_file_read()允许我们从文件中读取数据,apr_file_write()则是将数据写入到文件,这里是他们的原型定义:

/* excerpted from apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes);
APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes);

The third argument of both functions is value-result argument. It means, by which, we specify the input value's length on entry and get the output result's length on exit. In particular, apr_file_read() returns the length of read bytes and apr_file_write() returns the length of written bytes. Here is a sample code.

这两个函数的三个参数都是值参数(value-result argument.)我们指定了输入输出的长度,特别是apr_file_read()返回的是实际读入的长度,而apr_file_write()返回的是实际写入的长度单位是字节,下面是示例代码:

/* pseudo code about apr_file_write() */
strcpy(outbuf, "123456789");
apr_size_t outlen = strlen(outbuf);
rv = apr_file_write(fp, outbuf, &outlen);
printf("apr_file_write() rv = %d, nbytes = %d\n", rv, outlen);

In this case, before calling apr_file_write(), 'outlen' variable's value is 9. Passing in &outlen to apr_file_write() tells the API the writable length is 9. After returning from apr_file_write(), 'outlen' variable's value becomes actual written length. Usually, it is 9 if the file is a local file. Theoretically, it could become a smaller value (e.g. by disk full).

在这个例子中,在调用apr_file_write()函数之前,变量'outlen'的值是9,将'outlen'高速apr_file_write()获得写入长度是9。在apr_file_write()返回后,变量'outlen'的变成实际写入的长度。通常情况下实际写入的值会小于这个设定值。

We have to call apr_file_close() to close the file. Rather than that, we can implicitly close the file by destroying the memory pool that is passed to apr_file_open(). I prefer explicit closing to such an implicit way. It's just my opinion.

我们需要调用apr_file_close()来关闭一个已打开的文件,比这种方式更好的是,我们可以在打开文件时候传入一个内存池对象给apr_file_open(),这样在打开销毁内存池的时候,所有打开的文件会被强制关闭,我跟喜欢使用这样的方法关闭文件,不过这仅仅是我的个人的意见。

REMARK: There are some source code compatibility issues among libapr versions. The third argument of apr_file_open() has APR_FOPEN_ prefix after libapr-1.1.0, althougth it formerly didn't. We should use APR_FOPEN_CREATE instead of APR_CREATE. Please see apr_file_io.h of your using version. Similarly, the fourth argument of apr_file_open() has APR_FPROT_ prefixes after libapr-1.1.0.

备注:这有一些在APR各个版本间兼容的问题。apr_file_open()的第三个参数在APR 1.1.0版本之后会有APR_FOPEN_的前缀,虽然以前没有这个前缀,我应该使用APR_FOPEN_CREATE来代替APR_CREATE,请在使用前,开一下你所用版本中apr_file_io.h的定义。和第三个参数类似,apr_file_open()的第四个参数在APR 1.1.0后会有APR_FPROT_的前缀。

REMARK: There is a portability issue about file path separator. Unix(POSIX) uses slash('/'), and MS-Windows uses backslash('\') as separator. If you write an application program for both Unix and MS-Windows, I recommend you to canonicalize file pathes to use slash('/') as separators, because MS-Windows accepts it.

备注:这有一个关于文件路径分割符的问题,在Unix(POSIX)这样系统文件分割符之用的是“/”,而在MS-Windows中使用“\”作为文件分割符。由于你写的应用程序同时在Unix和MS-Windows上运行,我推荐是使用“/”作为文件路径的分割符,因为Windows也可以接受它。

REMARK: Be careful about apr_file_gets() usage. Calling apr_file_gets() without APR_BUFFERED severely hits performance. This is because that apr_file_gets() internally calls apr_file_read() per one byte. Remember you must open file with APR_BUFFERED flag when you use apr_file_gets().

备注:在使用apr_file_gets()请注意,如果在打开文件时没有使用APR_BUFFERED,那么在调用apr_file_gets()将会影响性能,这是因为apr_file_gets()是通过调用apr_file_read()每一次读取1个字节实现的,推荐你在需要调用apr_file_gets().的时候在打开文件时使用APR_BUFFERED标志位。

I recommend you to specify APR_BUFFERED flag except the following cases:

  • When you mmap the file (it causes an error to mmap APR_BUFFERED file)
  • No read/write (e.g. a file only for lock purpose)
  • You are sure that you reads/writes with a buffer big enough

我建议你在一下情况外最好使用APR_BUFFERED标志位

  • 当你在内存映射一个文件的时候,可以不使用APR_BUFFERED标志位(因为这可能会引起一个错误)
  • 不需要读写文件的时候(例如只以实现文件锁为目的时候),APR_BUFFERED标志位
  • 你确信在使用一个足够大的缓存进行读取的时候

REMARK: While you open file with APR_BUFFERED flag and if you call apr_file_trunc() for the file, you must call apr_file_flush() before apr_file_trunc(). Otherwise, the file becomes broken.

备注:当你使用APR_BUFFERED 标志位但开一个文件后,如果你学要掉用apr_file_trunc(),你必须在调用apr_file_trunc()前,调用apr_file_flush(),否则这个文件将会损坏。

REMARK: When you open file with APR_BUFFERED flag and the file is shared by multiple threads, APR_XTHREAD flag is also required for the file. Unfortunately, APR_XTHREAD flag on Windows has a side effect. My experiences tell me not to use APR_XTHREAD flag on Windows.

备注:当你使用APR_BUFFERED标志位打开一个文件并且这个文件是被多个线程共享使用的时候,APR_XTHREAD标志位也是需要在打开文件时候使用的。不幸的是APR_XTHREAD标志为在Windows上不会生效,我的经验告诉我不要在Windows上使用APR_XTHREAD标志位。

We can get file information such as size, timestamp, owner, access permission, and so on. Such the information are in apr_finfo_t structure, which we find in apr_file_info.h. There are two related APIs as follows:

我们可以获得如文件大小,时间信息,文件所有者,访问权限等这样信息,这样的信息被存储在apr_finfo_t结构中,我们可以在apr_file_info.h中找到他,有两个相关的API如下:

/* excerpted from apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted, apr_file_t *thefile);

/* excerpted from apr_file_info.h */
APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname, apr_int32_t wanted, apr_pool_t *pool);

apr_file_info_get() requires apr_file_t object, and apr_stat() requires file name. If we have already opened file and have apr_file_t object, it is better to use apr_file_info_get(). Otherwise, we have to call apr_stat(). Unlike other many types, apr_finfo_t is complete type. Rather than calling API to create object, we have to allocate the apr_finfo_t memory explicitly. Typically, it's allocated in local stack, because what we want to know might be some of the attributes such as file size or timestamp. Note that some memories, e.g. apr_finfo_t::fname, are allocated in the memory pool. This would cause a serious bug. Be careful. Please take a look at finfo-sample.c about the usage.

apr_file_info_get()需要一个apr_file_t对象,而apr_stat()需要的是一个文件名。如果我们有一个已经打开的文件对象(apr_file_t),最好使用apr_file_info_get()获取文件信息,否则我们需要使用apr_stat()。和其他许多种类型不同,apr_finfo_t是一个完整类型(complete type),不同于其他的API来创建对像,我们需要为apr_finfo_t分配明确的内存,典型应用是将他分配在本地栈上(local stack),因为我们知道它包含的属性能有那些如文件文件大小或时间信息。注意一些属性如apr_finfo_t::fname,会被分配在内存池中。这可能导致一系列的错误,请小心使用,可以参照finfo-sample.c关于他的具体使用方法。

There are some file handling APIs that works based on file names. For example, apr_file_remove() and apr_file_copy(). You can find them in apr_file_io.h and apr_file_info.h.

有一些文件处理的API是基于文件名的,如apr_file_remove()和apr_file_copy()。你可以在apr_file_io.h和apr_file_info.h文件中找到他们。

REMARK: Some APIs have 'wanted' argument that we specify bit-wised flag to get file attributes. The APIs are apr_dir_read(), apr_stat(), apr_lstat(), and apr_file_info_get(). Note that the value of 'wanted' argument could be beyond the OS file system support. and in such a case, the API returns APR_INCOMPLETE.

备注:有一些API拥有一个‘wanted’参数,我们可以通过比特形式的标志为获得文件属性,这些API如apr_dir_read(), apr_stat(), apr_lstat()和apr_file_info_get(),注意这些标志位参数可能超越操作系统的支持,这样的情况API将返回APR_INCOMPLETE。