统一xls/xlsx/csv的workbook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# -*- coding: utf-8 -*-
import os
import openpyxl # 解析xlsx
import xlrd # 解析xls
import csv # 解析csv


"""
类功能概述:
本代码实现了一个支持读取多种格式(xlsx、xls、csv)Excel 文件的功能。
主要包含 `Workbook` 和 `Sheet` 相关类,提供了统一的接口来操作不同格式文件。

使用步骤:
1. 创建 `Workbook` 对象,传入文件路径,根据文件后缀自动选择合适的解析器。
2. 通过 `Workbook` 对象的 `sheet_by_index` 方法获取指定索引的工作表(`Sheet` 对象)。
3. 使用 `Sheet` 对象的 `cell_value` 方法获取指定单元格的值,`max_row` 方法获取最大行数,`max_col` 方法获取最大列数。
"""


class Sheet:
"""
`Sheet` 类是所有工作表类的基类,定义了获取单元格值、最大行数和最大列数的接口。

方法:
cell_value(row, col): 获取指定行和列的单元格值。
max_row(): 获取工作表的最大行数。
max_col(): 获取工作表的最大列数。
"""

def cell_value(self, row, col):
pass

def max_row(self):
pass

def max_col(self):
pass


class XlsxSheet(Sheet):
def __init__(self, sheet):
self.sheet = sheet

def cell_value(self, row, col):
return self.sheet.cell(row=row+1, column=col+1).value

def max_row(self):
return self.sheet.max_row

def max_col(self):
return self.sheet.max_column


class XlsSheet(Sheet):
def __init__(self, sheet):
self.sheet = sheet

def cell_value(self, row, col):
return self.sheet.cell_value(row, col)

def max_row(self):
return self.sheet.nrows

def max_col(self):
return self.sheet.ncols


class CsvSheet(Sheet):
def __init__(self, rows):
self.rows = rows

def cell_value(self, row, col):
if row < len(self.rows) and col < len(self.rows[row]):
return self.rows[row][col]
return None

def max_row(self):
return len(self.rows)

def max_col(self):
max_cols = 0
for row in self.rows:
max_cols = max(max_cols, len(row))
return max_cols


class _Workbook:
def sheet_by_index(self, index):
pass


class XlsxFile(_Workbook):
def __init__(self, path):
self.workbook = openpyxl.load_workbook(path)

def sheet_by_index(self, index):
sheet = self.workbook[self.workbook.sheetnames[index]]
return XlsxSheet(sheet)


class XlsFile(_Workbook):
def __init__(self, path):
self.workbook = xlrd.open_workbook(path)

def sheet_by_index(self, index):
sheet = self.workbook.sheet_by_index(index)
return XlsSheet(sheet)


class CsvFile(_Workbook):
def __init__(self, path):
encodings = ['utf-8-sig', 'utf-8']
for encoding in encodings:
try:
with open(path, 'r', encoding=encoding) as file:
reader = csv.reader(file)
self.rows = list(reader)
break
except UnicodeDecodeError:
continue
else:
raise UnicodeDecodeError("无法使用支持的编码读取文件")

def sheet_by_index(self, index):
if index == 0:
return CsvSheet(self.rows)
return None


class Workbook:
"""
`Workbook` 类是对外提供的工作簿类,根据文件后缀自动选择合适的解析器。

属性:
impl: 具体的工作簿实现对象(`XlsxFile`、`XlsFile` 或 `CsvFile`)。

方法:
__init__(path): 初始化工作簿对象,根据文件后缀选择合适的解析器。
sheet_by_index(index): 获取指定索引的工作表。
"""

def __init__(self, path):
filedir, filename = os.path.split(path)
_, extname = os.path.splitext(filename)
if extname == ".xlsx":
self.impl = XlsxFile(path)
elif extname == ".xls":
self.impl = XlsFile(path)
elif extname == ".csv":
self.impl = CsvFile(path)
else:
raise IOError("未知的文件类型")

def sheet_by_index(self, index):
return self.impl.sheet_by_index(index)