ZetCode

Python os.open 函数

上次修改时间:2025 年 4 月 11 日

本综合指南探讨 Python 的 os.open 函数,该函数提供底层文件操作。我们将介绍文件描述符、标志、模式以及实际的文件处理示例。

基本定义

os.open 函数打开一个文件并返回一个文件描述符。它提供的控制级别比内置的 open 函数更低。

关键参数:path(要打开的文件),flags(打开模式标志),mode(新文件的权限位)。返回已打开文件的整数文件描述符。

基本文件打开

os.open 最简单的用法是打开一个文件进行读取。此示例演示了使用读取操作的基本文件描述符用法。

basic_open.py
import os

# Open file for reading only
fd = os.open("example.txt", os.O_RDONLY)

# Read content using file descriptor
content = os.read(fd, 1024)
print(f"File content: {content.decode('utf-8')}")

# Close the file descriptor
os.close(fd)

此示例以只读模式打开一个文件,读取其内容,然后关闭它。请注意,我们必须手动关闭文件描述符以避免资源泄漏。

os.read 操作返回字节,因此我们将其解码为 UTF-8 以进行字符串输出。第二个参数是要读取的最大字节数。

创建和写入文件

我们可以使用适当的标志创建新文件或截断现有文件。此示例演示了文件创建和写入操作。

create_write.py
import os

# Create or truncate file with write permissions
fd = os.open("output.log", os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644)

# Write data to file
data = "This is test data\n".encode('utf-8')
os.write(fd, data)

# Close file descriptor
os.close(fd)

print("File created and written successfully")

flags 组合在文件不存在时创建文件 (O_CREAT),在文件存在时截断文件 (O_TRUNC),并仅打开文件进行写入 (O_WRONLY)。

模式 0o644 设置标准 Unix 权限(所有者读/写,其他人读)。请注意权限位的八进制表示法(前导 0o)。

追加到文件

要附加到现有文件而不截断,我们使用 O_APPEND 标志。这可确保所有写入自动转到文件的末尾。

append_file.py
import os

# Open file for appending
fd = os.open("output.log", os.O_WRONLY | os.O_APPEND)

# Append data to file
new_data = "Appended line\n".encode('utf-8')
os.write(fd, new_data)

# Close file descriptor
os.close(fd)

print("Data appended to file")

O_APPEND 标志确保原子附加操作,防止多个进程写入同一文件之间的竞争条件。

如果没有 O_APPEND,我们需要在写入之前手动寻址到末尾,这可能会导致多进程场景中的竞争条件。

同时读取和写入

对于需要同时进行读写访问的文件,我们将 O_RDWR 与其他标志组合。此示例显示了读取和修改文件内容。

read_write.py
import os

# Open file for both reading and writing
fd = os.open("data.txt", os.O_RDWR)

# Read current content
content = os.read(fd, 1024)
print(f"Original content: {content.decode('utf-8')}")

# Move to beginning of file
os.lseek(fd, 0, os.SEEK_SET)

# Write new content
new_content = "Updated data\n".encode('utf-8')
os.write(fd, new_content)

# Close file descriptor
os.close(fd)

我们使用 O_RDWR 打开文件进行读写。读取后,我们使用 os.lseek 返回到文件的开头,然后再进行写入。

请注意,文件位置在读取和写入操作之间共享,因此在它们之间切换时必须仔细管理位置。

独占文件创建

O_EXCL 标志确保原子文件创建,如果文件存在则失败。这对于创建锁文件或确保唯一文件创建非常有用。

exclusive_create.py
import os

try:
    # Try to create file exclusively
    fd = os.open("lockfile", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
    print("File created exclusively")
    
    # Write to our new file
    os.write(fd, b"Lock file content")
    os.close(fd)
    
except FileExistsError:
    print("File already exists - cannot create exclusively")

O_EXCL 标志与 O_CREAT 组合可确保仅在文件不存在时才创建文件。如果文件存在,则引发 FileExistsError。

此模式通常用于锁文件,其中存在性检查和创建必须是原子的,以防止竞争条件。

非阻塞文件操作

O_NONBLOCK 标志启用非阻塞 I/O 操作。这在使用 FIFO 或设备文件等特殊文件时特别有用。

non_blocking.py
import os
import time

# Create a named pipe (FIFO)
fifo_path = "test.fifo"
try:
    os.mkfifo(fifo_path)
except FileExistsError:
    pass

# Open FIFO in non-blocking mode
try:
    fd = os.open(fifo_path, os.O_RDONLY | os.O_NONBLOCK)
    print("FIFO opened in non-blocking mode")
    
    # Attempt to read (won't block)
    try:
        data = os.read(fd, 1024)
        print(f"Read data: {data}")
    except BlockingIOError:
        print("No data available (non-blocking)")
    
    os.close(fd)
except OSError as e:
    print(f"Error opening FIFO: {e}")

此示例演示了使用命名管道的非阻塞 I/O。如果没有可用数据,读取操作会引发 BlockingIOError 而不是阻塞。

非阻塞模式对于无法承担 I/O 操作阻塞的事件驱动程序至关重要,例如网络服务器或 GUI 应用程序。

文件描述符继承

使用 os.open 打开的文件描述符默认由子进程继承。此示例演示了控制继承。

fd_inheritance.py
import os
import subprocess

# Open file normally (inheritable by default)
fd = os.open("inherited.txt", os.O_WRONLY | os.O_CREAT, 0o644)
os.write(fd, b"This will be inherited\n")

# Open file with close-on-exec flag
fd_noinherit = os.open("not_inherited.txt", 
                      os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC, 0o644)
os.write(fd_noinherit, b"This won't be inherited\n")

# Launch child process
subprocess.run(["ls", "-l"])

# Close files
os.close(fd)
os.close(fd_noinherit)

O_CLOEXEC 标志(close-on-exec)阻止文件描述符被子进程继承。这对于安全性和资源控制非常重要。

如果没有 O_CLOEXEC,子进程可能会无意中访问它们不应该访问的文件或耗尽文件描述符限制。

安全注意事项

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。迄今为止,我撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程