MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

PythonCSV文件的读写

2023-05-315.0k 阅读

一、CSV 文件简介

CSV(Comma - Separated Values),即逗号分隔值,是一种常用的文本文件格式,用于存储表格数据。它以纯文本形式存储数据,每行代表一条记录,字段之间通常用逗号分隔。这种简单的格式使得它在不同的软件和编程语言之间易于交换数据。例如,电子表格软件(如 Microsoft Excel、Google Sheets)可以轻松地导入和导出 CSV 文件。

CSV 文件的基本结构如下:

header1,header2,header3
value11,value12,value13
value21,value22,value23

第一行通常是表头,定义了每列数据的含义。后续每行是具体的数据记录。虽然逗号是最常见的分隔符,但有些 CSV 文件也可能使用其他字符,如制表符(\t)、分号(;)等。

二、Python 中读取 CSV 文件

在 Python 中,有多种方式可以读取 CSV 文件,最常用的是使用内置的 csv 模块。

2.1 使用 csv.reader

csv.readercsv 模块中用于读取 CSV 文件的主要函数之一。它将 CSV 文件的每一行解析为一个列表,列表中的每个元素对应一个字段。

import csv

with open('example.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        print(row)

在上述代码中:

  • import csv 导入了 csv 模块。
  • open('example.csv', 'r', newline='') 以只读模式打开名为 example.csv 的文件。newline='' 是为了避免在 Windows 系统下出现额外的空行问题。
  • csv.reader(csvfile) 创建了一个 reader 对象,用于逐行读取 CSV 文件。
  • for row in reader 遍历 reader 对象,row 就是 CSV 文件中的每一行数据,以列表形式呈现。

如果 CSV 文件的分隔符不是逗号,例如是分号,可以通过 delimiter 参数指定:

import csv

with open('example.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile, delimiter=';')
    for row in reader:
        print(row)

2.2 读取表头和数据

通常,CSV 文件的第一行是表头,后续行是数据。可以将表头和数据分开处理。

import csv

with open('example.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    headers = next(reader)
    data = list(reader)

print("Headers:", headers)
print("Data:", data)

这里,next(reader)reader 对象中获取第一行,即表头。然后,使用 list(reader) 将剩余的行转换为列表形式的数据。

2.3 使用 DictReader

csv.DictReader 提供了一种更方便的方式来读取 CSV 文件,它将每一行数据映射为一个字典,字典的键是表头中的字段名。

import csv

with open('example.csv', 'r', newline='') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        print(row)

例如,如果 CSV 文件如下:

name,age
Alice,25
Bob,30

使用 DictReader 读取时,row 将是类似 {'name': 'Alice', 'age': '25'} 的字典。这样可以通过字段名直接访问对应的值,而不需要记住列的索引。

三、Python 中写入 CSV 文件

Python 的 csv 模块同样提供了方便的函数来写入 CSV 文件。

3.1 使用 csv.writer

csv.writer 用于将数据写入 CSV 文件。

import csv

data = [
    ['name', 'age'],
    ['Alice', 25],
    ['Bob', 30]
]

with open('output.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    for row in data:
        writer.writerow(row)

在这段代码中:

  • data 是一个包含表头和数据行的列表。
  • open('output.csv', 'w', newline='') 以写入模式打开 output.csv 文件。
  • csv.writer(csvfile) 创建一个 writer 对象。
  • writer.writerow(row) 将每一行数据写入 CSV 文件。

如果要写入的数据包含特殊字符或需要进行更复杂的格式化,可以设置 quoting 参数。例如,quoting=csv.QUOTE_ALL 会将所有字段都用引号括起来。

import csv

data = [
    ['name', 'age'],
    ['Alice, Jr.', 25],
    ['Bob "The Great"', 30]
]

with open('output.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile, quoting=csv.QUOTE_ALL)
    for row in data:
        writer.writerow(row)

3.2 使用 DictWriter

csv.DictWriter 用于将字典形式的数据写入 CSV 文件。首先需要定义表头,然后通过 writerow 方法写入每一行数据。

import csv

data = [
    {'name': 'Alice', 'age': 25},
    {'name': 'Bob', 'age': 30}
]

headers = ['name', 'age']

with open('output.csv', 'w', newline='') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=headers)
    writer.writeheader()
    for row in data:
        writer.writerow(row)

这里,fieldnames 定义了表头。writer.writeheader() 方法写入表头行,writer.writerow(row) 将字典形式的每一行数据写入文件。

四、处理复杂的 CSV 数据

4.1 处理缺失值

在实际的 CSV 数据中,经常会遇到缺失值的情况。当读取 CSV 文件时,可以对缺失值进行处理。例如,将缺失值替换为默认值。

import csv

default_value = 'N/A'
with open('example_with_missing.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    new_data = []
    for row in reader:
        new_row = [cell if cell else default_value for cell in row]
        new_data.append(new_row)

with open('output_with_filled.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    for row in new_data:
        writer.writerow(row)

在上述代码中,当读取到空单元格(cell 为空字符串)时,将其替换为 'N/A'

4.2 数据类型转换

CSV 文件中的数据默认都是字符串类型。在读取数据后,可能需要将某些字段转换为其他数据类型,如整数、浮点数等。

import csv

with open('example.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    headers = next(reader)
    data = []
    for row in reader:
        new_row = [int(row[1]) if row[1].isdigit() else row[1] for i, value in enumerate(row)]
        data.append(new_row)

print("Headers:", headers)
print("Data:", data)

这里假设第二列的数据是整数类型,通过 int(row[1]) if row[1].isdigit() else row[1] 将其转换为整数,如果无法转换则保持原字符串。

4.3 处理大 CSV 文件

对于大的 CSV 文件,一次性读取整个文件可能会导致内存不足。可以逐块读取和处理数据。

import csv

chunk_size = 1000
with open('large_example.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    headers = next(reader)
    chunk = []
    for i, row in enumerate(reader):
        chunk.append(row)
        if (i + 1) % chunk_size == 0:
            # 处理当前块的数据
            # 例如,可以将当前块写入另一个文件
            with open('chunk_output.csv', 'a', newline='') as outputfile:
                writer = csv.writer(outputfile)
                if i == chunk_size - 1:
                    writer.writerow(headers)
                writer.writerows(chunk)
            chunk = []
    # 处理剩余的数据
    if chunk:
        with open('chunk_output.csv', 'a', newline='') as outputfile:
            writer = csv.writer(outputfile)
            writer.writerows(chunk)

在这段代码中,每次读取 chunk_size 行数据进行处理,处理完后将数据写入另一个文件,从而避免一次性加载整个大文件。

五、CSV 文件与其他数据结构的交互

5.1 与列表的交互

如前面的示例所示,csv.reader 读取的数据可以很方便地转换为列表。同样,将列表数据写入 CSV 文件也很直接。另外,如果有一个复杂的嵌套列表结构,也可以将其转换为适合写入 CSV 的格式。

import csv

nested_list = [
    [1, 2, 3],
    [4, [5, 6], 7],
    [8, 9, 10]
]

flat_list = []
for sublist in nested_list:
    flat_row = []
    for item in sublist:
        if isinstance(item, list):
            flat_row.extend(item)
        else:
            flat_row.append(item)
    flat_list.append(flat_row)

with open('nested_to_csv.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    for row in flat_list:
        writer.writerow(row)

这里将一个嵌套列表展平为适合 CSV 格式的列表,然后写入文件。

5.2 与字典的交互

csv.DictReadercsv.DictWriter 已经展示了 CSV 文件与字典的交互。此外,还可以将一个字典列表转换为特定格式的 CSV 数据。例如,如果字典中的值是列表,需要展开写入。

import csv

data = [
    {'name': 'Alice', 'hobbies': ['reading', 'painting']},
    {'name': 'Bob', 'hobbies': ['sports']}
]

headers = ['name', 'hobby']

with open('dict_to_csv.csv', 'w', newline='') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=headers)
    writer.writeheader()
    for row in data:
        for hobby in row['hobbies']:
            writer.writerow({'name': row['name'], 'hobby': hobby})

在这个例子中,将每个字典中的 hobbies 列表展开,为每个爱好创建一行数据写入 CSV 文件。

5.3 与 Pandas DataFrame 的交互

Pandas 是 Python 中强大的数据处理库,DataFrame 是其核心数据结构。可以很方便地将 CSV 文件读取为 DataFrame,并对其进行各种操作,然后再写回 CSV 文件。

import pandas as pd

# 读取 CSV 文件为 DataFrame
df = pd.read_csv('example.csv')

# 对 DataFrame 进行操作,例如添加新列
df['new_column'] = df['age'] * 2

# 将 DataFrame 写回 CSV 文件
df.to_csv('output_with_new_column.csv', index=False)

这里使用 pd.read_csv 将 CSV 文件读取为 DataFrame,然后添加了一个新列 new_column,最后使用 to_csv 方法将修改后的 DataFrame 写回 CSV 文件,index=False 表示不将索引写入文件。

六、CSV 文件的编码问题

CSV 文件可能使用不同的编码,常见的编码有 UTF - 8、Windows - 1252 等。在读取和写入 CSV 文件时,需要指定正确的编码。

6.1 读取时指定编码

import csv

with open('example.csv', 'r', newline='', encoding='Windows - 1252') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        print(row)

这里通过 encoding='Windows - 1252' 指定文件的编码为 Windows - 1252。如果不指定正确的编码,可能会出现乱码问题。

6.2 写入时指定编码

import csv

data = [
    ['name', 'age'],
    ['Alice', 25],
    ['Bob', 30]
]

with open('output.csv', 'w', newline='', encoding='UTF - 8') as csvfile:
    writer = csv.writer(csvfile)
    for row in data:
        writer.writerow(row)

在写入文件时,通过 encoding='UTF - 8' 指定文件的编码为 UTF - 8,这是一种广泛使用的编码,能够处理多种语言的字符。

七、CSV 文件的安全性问题

7.1 防止注入攻击

类似于 SQL 注入攻击,在处理用户输入并写入 CSV 文件时,需要防止恶意用户通过输入特殊字符来破坏文件格式或执行恶意代码。例如,如果用户输入的字段值包含逗号,可能会导致 CSV 文件格式错乱。

import csv
import re

def sanitize_input(input_str):
    # 简单的清理,去除逗号等特殊字符
    return re.sub(r'[,\n\r]', '', input_str)

user_input = input("请输入名字: ")
sanitized_input = sanitize_input(user_input)

data = [
    ['name'],
    [sanitized_input]
]

with open('user_input.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    for row in data:
        writer.writerow(row)

在上述代码中,sanitize_input 函数通过正则表达式去除了输入字符串中的逗号、换行符等可能破坏 CSV 格式的字符。

7.2 数据隐私保护

当处理包含敏感信息(如个人身份证号、信用卡号等)的 CSV 文件时,需要采取措施保护数据隐私。一种常见的方法是对敏感数据进行加密。

from cryptography.fernet import Fernet
import csv

# 生成加密密钥
key = Fernet.generate_key()
cipher_suite = Fernet(key)

# 读取包含敏感信息的 CSV 文件
with open('sensitive.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    encrypted_data = []
    for row in reader:
        encrypted_row = []
        for cell in row:
            encrypted_cell = cipher_suite.encrypt(cell.encode())
            encrypted_row.append(encrypted_cell.decode())
        encrypted_data.append(encrypted_row)

# 将加密后的数据写入新的 CSV 文件
with open('encrypted_sensitive.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    for row in encrypted_data:
        writer.writerow(row)

这里使用 cryptography 库对 CSV 文件中的每个字段进行加密,然后将加密后的数据写入新的文件,从而保护敏感信息。

八、CSV 文件在不同操作系统上的差异

CSV 文件在不同操作系统(如 Windows、Linux、macOS)上的处理存在一些差异,主要体现在换行符和编码默认值上。

8.1 换行符差异

在 Windows 系统中,换行符是 \r\n,而在 Linux 和 macOS 系统中,换行符是 \n。当在不同系统间共享 CSV 文件时,可能会出现换行问题。在 Python 中,通过设置 newline='' 可以避免这个问题。例如:

import csv

# 在 Windows 系统上写入 CSV 文件
data = [
    ['name', 'age'],
    ['Alice', 25],
    ['Bob', 30]
]

with open('output.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    for row in data:
        writer.writerow(row)

这样无论在哪个操作系统上运行这段代码,生成的 CSV 文件都能在其他系统上正确读取。

8.2 编码默认值差异

不同操作系统对文件编码的默认设置可能不同。在 Windows 系统中,默认编码可能是 Windows - 1252,而在 Linux 和 macOS 系统中,默认编码通常是 UTF - 8。为了确保 CSV 文件在不同系统间的兼容性,建议在读取和写入文件时显式指定编码,如前面编码问题部分所述。

通过深入理解这些方面,开发者可以在 Python 中更加高效、安全地处理 CSV 文件,无论是简单的数据读取写入,还是复杂的数据处理和跨系统应用。