文件数据的表示方式
file
的定义如下,open操作也就是建立这个file对象,因此file是进程
维护的对象。
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
struct inode *f_inode; /* cached value */
const struct file_operations*f_op; /* 和文件关联的操作*/
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags; /*文件标志,如O_RDONLY、O_NONBLOCK、O_SYNC*/
fmode_t f_mode; /*文件读/写模式,FMODE_READ和FMODE_WRITE*/
struct mutex f_pos_lock;
loff_t f_pos; /* 当前读写位置 */
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_statef_ra;
u64 f_version;
#ifdef CONfiG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data; /*文件私有数据*/
#ifdef CONfiG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONfiG_EPOLL */
struct address_space*f_mapping;
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
inode
的定义如下,inode是由文件系统
维护的数据:
struct inode {
...
umode_t i_mode; /* inode的权限 */
uid_t i_uid; /* inode拥有者的id */
gid_t i_gid; /* inode所属的群组id */
dev_t i_rdev; /* 若是设备文件,此字段将记录设备的设备号 */
loff_t i_size; /* inode所代表的文件大小 */
struct timespec i_atime; /* inode最近一次的存取时间 */
struct timespec i_mtime; /* inode最近一次的修改时间 */
struct timespec i_ctime; /* inode的产生时间 */
unsigned int i_blkbits;
blkcnt_t i_blocks; /* inode所使用的block数,一个block为512 字节 */
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
/* 若是块设备,为其对应的block_device结构体指针 */
struct cdev *i_cdev; /* 若是字符设备,为其对应的cdev结构体指针 */
}
...
};
Linux-glibc
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#define BUFSIZE 512
int main()
{
char buffer[BUFSIZE];
int fd = open("/tmp/test.txt", O_RDONLY);
ssize_t n;
if (fd == -1) {
perror("open");
return -1;
}
while ((n = read(fd, buffer, BUFSIZE)) > 0) {
if (write(STDOUT_FILENO, buffer, n) == -1) {
perror("write");
close(fd);
return -1;
}
}
close(fd);
return 0;
}
Windows读写文件
#include <windows.h>
#include <iostream>
int main()
{
// 创建文件句柄
HANDLE hFile = CreateFile(TEXT("test.txt"), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "CreateFile failed, error code: " << GetLastError() << std::endl;
return 1;
}
// 写入数据
const char* data = "Hello, world!";
DWORD bytesWritten;
if (!WriteFile(hFile, data, strlen(data), &bytesWritten, NULL)) {
std::cerr << "WriteFile failed, error code: " << GetLastError() << std::endl;
CloseHandle(hFile);
return 1;
}
// 读取数据
char buf[1024];
DWORD bytesRead;
if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) {
std::cerr << "ReadFile failed, error code: " << GetLastError() << std::endl;
CloseHandle(hFile);
return 1;
}
buf[bytesRead] = '\0';
std::cout << "Read data: " << buf << std::endl;
// 关闭文件句柄
CloseHandle(hFile);
return 0;
}
C语言
C提供两种文件模式:文本模式和二进制模式。
之所以会提供两种模式,是因为不同的平台虽然字符集是相同的,但是换行符不同,在Windows上换行符是\r\n
,在Linux上换行符是\n
。所以C语言为了让数据能够适应平台,就必须在各个平台间进行转换。
在C语言中,文件读写操作主要涉及以下函数:
- fopen: 打开文件并返回文件指针。
- fclose: 关闭文件。
- fgetc/getc: 从文件中读取一个字符。
- fgets: 从文件中读取一行数据。
- fputc/putc: 将一个字符写入文件。
- fputs: 将字符串写入文件。
- fprintf: 格式化输出到文件。
- fscanf: 从文件中按格式读取数据。
int ch;
FILE *fp;
fp = fopen("a.txt","r");
ch = getc(fp);
fclose(fp);
FILE *fp;
int fd;
fd = open("a.txt","r");
fp = fdopen(fd,"rb");
fgets(buffer, sizeof(buffer), file);
fputs("Hello, World!", file);
C++
#include <iostream>
#include <fstream>
int main() {
// 打开文件
std::ifstream infile("example.txt");
// 读取文件内容
std::string line;
while (std::getline(infile, line)) {
std::cout << line << std::endl;
}
// 关闭文件
infile.close();
return 0;
}
python
# 打开文件
file = open('example.txt', 'w')
# 写入文件
file.write('Hello World!\n')
file.write('Goodbye World!\n')
# 关闭文件
file.close()
# 打开文件进行读取
file = open('example.txt', 'r')
# 读取文件
content = file.read()
print(content)
# 关闭文件
file.close()
Qt
#include <QFile>
#include <QTextStream>
int main()
{
// 写入文件
QFile file("test.txt");
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << "Hello, world!\n";
file.close();
}
// 读取文件
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
QString line = in.readLine();
qDebug() << line;
file.close();
}
return 0;
}
文本模式和二进制模式
文本模式和二进制模式只在Windows下生效,因为只有Windows会将文本的结束符添加为\r\n
,而c语言和linux中默认文本的换行符就是\n
,处理文本和处理二进制行为是一致的,所以linux系统调用中不存在设置二进制模式和文本模式的标志位,只有Windows下,c语言才需要将\r\n
结束符转换为\n
进行处理。
所以二进制模式只在Windows系统,c语言等其他语言情况下才有。
在linux系统上,使用c语言设置二进制模式和不设置二进制模式效果是一样的,都是原样读写,包括\r
也是原样读写。
vim 会自动识别dos文件:
如果以二进制读取则:
vim -b <文件>
查看二进制格式:
hexdump -C <文件>
00000000 5b 41 75 74 6f 52 75 6e 2e 41 6d 64 36 34 5d 0d |[AutoRun.Amd64].|
00000010 0a 6f 70 65 6e 3d 73 65 74 75 70 2e 65 78 65 0d |.open=setup.exe.|
00000020 0a 69 63 6f 6e 3d 73 65 74 75 70 2e 65 78 65 2c |.icon=setup.exe,|
00000030 30 0d 0a 0d 0a 5b 41 75 74 6f 52 75 6e 5d 0d 0a |0....[AutoRun]..|
00000040 6f 70 65 6e 3d 73 6f 75 72 63 65 73 5c 53 65 74 |open=sources\Set|
00000050 75 70 45 72 72 6f 72 2e 65 78 65 20 78 36 34 0d |upError.exe x64.|
00000060 0a 69 63 6f 6e 3d 73 6f 75 72 63 65 73 5c 53 65 |.icon=sources\Se|
00000070 74 75 70 45 72 72 6f 72 2e 65 78 65 2c 30 0d 0a |tupError.exe,0..|
00000080
会看到^M
其实是0a