# io 库文件操作

io 库提供标准输入输出，文件操作函数，  
  
如果是一次性读写文件数据，应优先选择：[string 库文件读写函数](../string/file.md)

## 打开或创建文件对象

### 1. 函数原型：   

```aardio
file = io.file( filepath, mode="r",shflag )
```
  
### 2. 函数说明：   
      
io.file 用于打开或创建文件对象。  
函数调用成功会返回文件对象，调用失败则会返回 null, 错误信息。

[io.file 对象参考手册](/library-reference/io/_.html#ioFileObject)

参数 @filepath 指定要打开的文件路径，如果文件路径参数为空（null 或 空字符串）则创建可读写的临时文件对象。

可选的 @shflag 参数用于指定文件共享或独占模式。

@shflag 支持以下选项： 

- `0x40 /* _SH_DENYNO */` 共享模式(默认模式),外部程序可读写该文件
- `0x10 /* _SH_DENYRW */ `独占读写权限
- `0x20 /* _SH_DENYWR */` 独占写权限
- `0x30 /* _SH_DENYRD */` 独占读权限
- `0x80 /* _SH_SECURE */` 安全模式

@mode 参数使用一个包含模式字符的字符串指定文件读写选项，可选模式如下：

- 基本模式：读、写、追加模式   
  * "r": 读模式(默认模式)，文件必须存在  
  * "w": 写模式,文件不存在就新建文件  
  * "a": 追加模式,文件不存在就新建文件，文件指针会移动到文件末尾。如希望打开文件用于写，又不要删除原文件中的数据，并从原文件的末尾开始添加新的数据，应该使用此模式。  
  
  必须使用基本模式 "r","w","a" 其中之一作为 @mode 参数的第一个字符，并且只能作为第一个字符。

- 读写复合模式  
  * "r+": 打开可读写的文件，该文件必须存在。  
  * "w+": 打开或创建可读写文件，若文件不存在则建立该文件。文件原先的内容会被清除。  
  * "a+": 以附加方式打开或创建可读写的文件，若文件不存在则会建立该文件。文件指针会移动到文件末尾，文件原先的内容会被保留(如果文件存在，写入的数据会被加到文件尾后)。  
    
  要点： 
  * r,r+ 都要求文件必须存在,  
  * w,w+,a,a+ 如果发现文件不存在则会创建文件.  
  * w 与 a 的区别在于: w 会清空原来的文件内容,而a不会  
  * 有 + 号与没 + 号的区别在于: 有 + 号表示可读可写，而没有 + 号要么是可读而不可写，要么是可写而不可读。

- 附加模式
  * "t" 文本模式，"b" 二进制模式  

    模式后添加 t 表示以文本模式访问,添加 b 表示以二进制模式访问。  
    例如 "rb" 表示以二进制读模式打开文件。  
      
    io.file 默认使用文本模式, 而 [string.load() string.save()](../string/file.md) 等函数则默认以二进制模式读写文件.  

      二进制模式同样可以读写文本文件，区别仅仅是：
    - 使用文本模式时 aardio 会保证读取的文本以`'\n'`换行( 参考:[回车换行符](../../../language-reference/datatype/newline.md) ),  
    - 文本模式打开文件时,`'\0'`,`'\x1A'`  都被认为是终止符。
    
    如果你要使用 io.file() 函数打开二进制文件读写，或者不需要自动转换换行格式，并且需要读取 `'\x1A'` 或者 `'\0'`，请务必显式指定'b'模式。
  * "S" 连续模式  ，"R" 随机模式
  * 'D' 打开为临时文件，关闭文件对象时自动删除文件。
  * css 附加模式，用于指定文件编码

      io.stdout,io.stderr,io.stdin 可以在读写模式中使用 ccs 字段指定文件的存储编码并在输入输出过程中支持自动转换编码， 例如"w,ccs=UTF-8","w,ccs=UTF-16LE","w,ccs=UNICODE"，但只能在将标准输出重定向到硬盘文件时才能正常使用，编码也只能指定这三种。
      
      当标准输入输出流重定向到控制台时只能指定普通文本模式不允许使用ccs标记， 使用ccs标记指定错误的编码，或者使用了二进制读写函数都会导致程序不可预料的异常。

      即使使用 css 模式指定了编码，如果文件指定了 BOM 仍然会以文件 BOM 实际声明的编码为准。

      aardio 不建议大家使用这个 css 模式, 因为 aardio 的默认编码就是UTF8，而且在 aardio 中转换编码也非常方便，一般并不需要用到这个不好控制的 css 模式。

### 3. 调用示例：   

  
  ```aardio
  //打开文件失败时会返回两个参数: null,错误信息
  var file,err = io.file("/nofile.txt", "rb"); 

  //断言
  assert( file,err );

  //读文件第一行数据
  var line = file.read();

  //关闭文件，对象回收时也会自动调用 close 函数
  file.close();
  ```  
  
  assert 函数在接收的第一个参数为 null 时断言失败，断言失败时会将第二个参数作为错误信息抛出异常。请参考: [assert](../../../language-reference/builtin-function/assert.md)


## 关闭文件

可使用 `file.close()` 函数关闭使用 io.file 打开的文件 file 对象。如果不调用 close 函数，在对象回收时也会触发析构函数并自动调用 close 函数关闭文件。 

## 写文件

我们可使用 file.write 函数写入数据到文件。

函数原型： 

```aardio
file.write(...);  
```
  
file.write 函数可指定任意个数的数值、字符串、或结构体(struct)作为参数，然后将它们写入到输出文件流。  

示例：

```aardio
var file = io.file("/test.txt","w+b");

file.write("hello" + "world"); //先连接字符串再写入是不必要的。
file.write("hello", "world"); //直接用多个参数更快更好
```  

file.write 的参数中如果是定义了原生数据类型的 [结构体](../raw/struct.md)，则会转换为二进制数据并直接写入文件。

## 输出缓冲

file.flush() 函数输出缓冲区内容到文件中。调用 file.flush 函数或者 file.close 函数都会将缓冲区的内容立即保存到文件。

## 读文件

函数原型：

```aardio
file.read(...)  
```
  
file.read 函数从当前输入文件读取数据，由它的参数控制读取的内容。

file.read 的参数可以有任意多个，参数可设置的选项如下：  
  
- "%s" 读取下一行文本 

    示例：

    ```aardio
    var str = file.read("%s") 
    ```

    函数返回当前输入文件的下一行（不包含最后的换行符）。    
    当到达文件末尾，返回值为 null（表示没有下一行可返回）。    
    该读取方式 是read 函数的默认方式，所以可以简写为 `file.read()`。  

- "%d" 读取字符串并转换为数值  

    示例：

    ```aardio
    var int = file.read("%d")
    ```

函数从当前输入文件中读取出一个数值(而不是字符串)。   
当需要从一个文件中读取大量数字时，数字间的字符串为空白可以显著的提高执行性能。 "%d" 选项会跳过两个可被识别数字之间的任意空格。这些可识别的字符串可以是`-3`, `+5.2`, `1000`,和 `-3.4e-23`。如果在当前位置找不到一个数字（由于格式不对，或者是到了文件的结尾），则返回 null  。
  
可以对 file.read 的每个参数设置不同的选项，函数将返回各自的结果。例如： `var n1,n2,n3 = file.read("%d","%d","%s")`

- 数值参数用于指定读取的字节长度


    示例：

    ```aardio
    str = file.read(n)
    ```

  file.read 函数将尝试从输入文件中读取n个字节。  
  如果无法读取到任何字符（已经到了文件末尾），函数返回 null。否则返回一个最多包含n个字符的串。

  读取长度指定为 0 时可用于测试是否到达到用来测试是否到达了文件末尾。如果不是返回一个空串，如果已是文件末尾返回 null。

  示例：

  ```aardio
  var eof = file.read(0)  
  ```

  读取长度如果为负数，表示从当前位置读取内容到文件尾部倒计数的指定位置，失败返回 null。

  示例：

  ```aardio
  var str = file.read(-1); //读取到文件末尾
  ```

  读取长度为 `-1` 表示读取到文件尾部。要特别注意：当读取长度参数为 `-1` 且读取失败时也会返回空字符串而不是返回 null 值 。

- 参数为结构体时，file 对象将二进制数据读取到结构体

  示例：

  ```aardio
  var struct = file.read({int x;int y})
  ```

  函数获取一个 [结构体](../raw/struct.md) 的长度,并从文件中读取相同长度的二进制数据到结构体中。 如果当前位置在文件末尾，或者文件为空，函数将返回 null。

## 反向读文件

函数原型：

```aardio
var str = file.readback(n)  
```
  
此函数与 file.read 类似,不同的是它从后向前移动文件指针并读取指定参数 n 指定字节长度的内容，如果没有指定长度,则向前读取一行.  
  
此函数不支持多参数，也不支持除数值以外的参数。
  
- 参数如果是0，测试是否到达文件头，返回值可用于条件表达式。  
- 参数如果是正数 n，则从当前位置向前读取n个字节。  
- 参数如果是负数 `-n`，则从当前位置向前读取到文件头倒数 n 个字节所在位置。

无参数则向前读取一行。  
不支持其他格式参数，不支持多参数。  

## 移动文件指针

函数原型：

```aardio
file.seek(whence="cur",offset=0)  
```
 
whence 参数指定偏移方式，offset 参数指定偏移量。  
返回值为读写指针的当前位置。

whence 参数可选项如下  
- "set"，偏移值是从文件头开始；  
- "cur",偏移值从当前位置开始；  
- "end"，偏移值从文件尾往前计数

whence 的默认值为"cur"，offset的默认值为 0。如果不指定任何参数使用 `var offset = file.seek()` 得到的返回值就是文件当前的存取位置，且保持原位置不变（不会移动文件指针）。  

`file.seek("set")` 会将文件的存取位置重设到文件开头。  
而 `offset = file.seek("end")` 会将文件指针移动到文件尾。

如果需要得到文件的大小又不改变读写指针的位置，可以使用 file.size() 函数。

## 设置缓冲区

函数原型：

```aardio
file.setvbuf (mode [, size])  
```

设置写入文件流使用的缓冲区，  
第一个参数指定缓冲区模式，支持以下三种模式:  

- "no": 无缓冲区.  
- "full": 使用完全缓冲  
- "line": 使用行缓冲  

可选用第二个参数指定缓冲区大小。