<think>我们遇到了一个错误:在创建名为'01'的sheet页时,尝试覆盖单元格(0,0)。这是因为在同一个sheet页中,我们可能多次尝试在同一个位置写入数据。
回顾代码:我们为每个前缀创建一个sheet页,然后在该sheet页中为每个文件创建一个表格。每个表格包括一个合并的标题行(占据0到4列)和表头行。
问题在于,当我们处理同一个sheet页中的多个文件时,我们使用一个变量`current_row`来跟踪当前写入的行。但是,在写入第一个文件后,`current_row`会递增,然后处理下一个文件时,会从新的行开始写入。所以理论上不应该覆盖。
但是,请注意错误发生在写入第一个文件的标题行时(行0,列0)。在同一个sheet页中,第一个文件写入时,`current_row`从0开始,然后写入标题行(合并单元格,位置为0行0列到0行4列),然后递增`current_row`。下一个文件会从新的行开始。
所以,为什么会出现覆盖?可能是因为在同一个sheet页中,我们处理多个文件时,每个文件都从0行开始?不,我们使用的是同一个`current_row`变量,并且每个文件都会更新它。
然而,错误信息显示是在写入第一个文件时就出错了?实际上,错误信息显示是在写入合并单元格时(`write_merge`)时,在(0,0)位置尝试写入,但是已经有一个单元格存在了。
我们再看代码:在写入合并单元格之前,我们先用`ws.write`写了5个单元格(0,0到0,4),然后紧接着又用`write_merge`覆盖了这5个单元格。这可能是导致问题的原因吗?
实际上,`write_merge`会写入一个合并的单元格,它应该覆盖之前写入的单个单元格。但是,xlwt不允许在同一个位置重复写入(即使之前写的是空值)。所以,我们之前用`ws.write`写了5个单元格,然后立即用`write_merge`写同一个区域,这就会导致冲突。
解决方案:我们不应该先写5个单元格,然后再合并。应该直接使用`write_merge`来写入标题。
修改前:
```python
ws.write(current_row, 0, "文件: {}".format(filename), title_style)
ws.write(current_row, 1, "", title_style) # 占位空单元格
ws.write(current_row, 2, "", title_style)
ws.write(current_row, 3, "", title_style)
ws.write(current_row, 4, "", title_style)
# 然后
ws.write_merge(...)
```
这会导致先写入5个单元格,然后立即尝试合并并覆盖,从而引发冲突。
修改:直接使用`write_merge`,去掉前面的5个`write`。
所以,我们将代码修改为:
```python
# 合并标题单元格
ws.write_merge(
current_row, current_row, 0, 4,
"文件: {}".format(filename),
xlwt.easyxf('font: bold on, height 280; align: wrap on, vert centre')
)
current_row += 1
```
这样,我们只写一次,就不会有冲突了。
另外,我们还需要注意,同一个sheet页中,每个文件的表格是连续的,所以不会出现重叠。但是,每个文件表格的起始行是递增的,所以不会重叠。
因此,修改后的代码段如下:
```python
# 写入文件名标题
title_style = xlwt.easyxf('font: bold on, height 280; align: wrap on, vert centre')
# 合并标题单元格
ws.write_merge(
current_row, current_row, 0, 4,
"文件: {}".format(filename),
title_style
)
current_row += 1
```
这样修改后,应该可以避免覆盖单元格的错误。
另外,我们还需要注意,如果同一个sheet页中没有任何文件,那么就不会创建该sheet页(因为我们在循环`all_filenames`,而`all_filenames`为空则跳过)。所以不会出现空sheet页。
现在,我们修改代码并重新运行。
同时,我们还需要注意,错误信息中提到的sheet名为'01',这是因为我们提取前缀时,文件名以数字开头(如01_mount.txt),提取前缀为'01_',然后我们去掉末尾下划线得到'01'作为sheet名。
所以,修改后的代码段如下(只修改了写入标题的部分):</think>### 问题分析与解决方案
您遇到的错误是Excel单元格覆盖问题,具体原因是`write_merge()`方法在写入合并单元格时与之前写入的单元格冲突。以下是修复后的完整脚本:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
import sys
import xlwt
from collections import defaultdict
def parse_du_file(file_path):
"""解析du命令输出文件"""
data = {}
try:
with open(file_path, 'r') as f:
for line in f:
if 'Permission denied' in line or 'No such file' in line or not line.strip():
continue
match = re.match(r'(\d+\.?\d*)\s*([KMG]?)[Bb]?\s+(.*)', line.strip())
if match:
size, unit, path = match.groups()
size = float(size)
# 单位转换
if unit == 'M': size *= 1024
elif unit == 'G': size *= 1024*1024
elif unit == '': size = size/1024
data[path] = round(size, 2)
except IOError as e:
print("警告: 无法读取文件 {}: {}".format(file_path, str(e)))
return data
def extract_file_prefix(filename):
"""提取文件前缀"""
match = re.match(r'^(\d+_)', filename)
return match.group(1) if match else "other_"
def generate_comparison_report(folder1, folder2, output_xlsx):
"""生成存储对比报告"""
# 验证文件夹是否存在
for folder in [folder1, folder2]:
if not os.path.exists(folder):
print("错误: 目录不存在 - {}".format(folder))
return "目录 {} 不存在,请检查路径".format(folder)
if not os.path.isdir(folder):
print("错误: 路径不是目录 - {}".format(folder))
return "{} 不是有效目录".format(folder)
# 初始化数据结构
machine1_data = defaultdict(dict) # {prefix: {filename: data}}
machine2_data = defaultdict(dict)
# 收集数据
for folder_path, data_dict in [(folder1, machine1_data), (folder2, machine2_data)]:
print("处理目录: {}".format(folder_path))
try:
for filename in os.listdir(folder_path):
if filename.endswith('.txt'):
file_path = os.path.join(folder_path, filename)
print("解析文件: {}".format(file_path))
# 提取文件前缀
prefix = extract_file_prefix(filename)
# 解析文件数据
file_data = parse_du_file(file_path)
data_dict[prefix][filename] = file_data
except OSError as e:
print("目录访问错误: {}".format(str(e)))
return "无法访问目录 {}: {}".format(folder_path, str(e))
# 创建Excel工作簿
try:
wb = xlwt.Workbook(encoding='utf-8')
header_style = xlwt.easyxf('font: bold on')
title_style = xlwt.easyxf('font: bold on, height 280; align: wrap on, vert centre')
normal_style = xlwt.easyxf()
# 获取所有前缀并排序
all_prefixes = sorted(set(machine1_data.keys()) | set(machine2_data.keys()))
# 为每个前缀创建Sheet页
for prefix in all_prefixes:
# 创建Sheet页(修复名称长度问题)
sheet_name = prefix.rstrip('_') # 去掉末尾下划线
if len(sheet_name) > 31: # Excel sheet名称长度限制
sheet_name = sheet_name[:31]
ws = wb.add_sheet(sheet_name)
print("创建Sheet页: {}".format(sheet_name))
# 当前行指针
current_row = 0
# 获取当前前缀下的所有文件名
all_filenames = sorted(
set(machine1_data.get(prefix, {}).keys()) |
set(machine2_data.get(prefix, {}).keys())
)
# 为每个文件创建独立表格
for filename in all_filenames:
# 修复:直接使用write_merge,避免先写单元格再合并
ws.write_merge(
current_row, current_row, 0, 4,
"文件: {}".format(filename),
title_style
)
current_row += 1
# 写入表头
headers = ['路径', '机器1大小(KB)', '机器2大小(KB)', '差值(KB)', '差异百分比(%)']
for col, header in enumerate(headers):
ws.write(current_row, col, header, header_style)
current_row += 1
# 获取文件数据
data1 = machine1_data.get(prefix, {}).get(filename, {})
data2 = machine2_data.get(prefix, {}).get(filename, {})
all_paths = sorted(set(data1.keys()) | set(data2.keys()))
# 写入数据行
for path in all_paths:
size1 = data1.get(path, 0.0)
size2 = data2.get(path, 0.0)
diff = size2 - size1
if size1 != 0:
diff_pct = (diff / size1) * 100
else:
diff_pct = float('inf') if diff != 0 else 0.0
# 写入行数据
ws.write(current_row, 0, path, normal_style)
ws.write(current_row, 1, size1, normal_style)
ws.write(current_row, 2, size2, normal_style)
ws.write(current_row, 3, diff, normal_style)
ws.write(current_row, 4, diff_pct, normal_style)
current_row += 1
# 添加两个空行分隔表格
current_row += 2
# 保存文件
wb.save(output_xlsx)
return "对比报告已成功生成: {}".format(output_xlsx)
except Exception as e:
import traceback
traceback.print_exc()
return "生成Excel文件时出错: {}".format(str(e))
if __name__ == "__main__":
# 使用当前目录下的相对路径
report = generate_comparison_report(
folder1='./moscow', # 当前目录下的moscow文件夹
folder2='./tacoof', # 当前目录下的tacoof文件夹
output_xlsx='file_prefix_compare.xlsx'
)
print(report)
```
### 关键修复点:
1. **单元格写入冲突修复**:
```python
# 修复前(错误写法)
ws.write(current_row, 0, "文件: {}".format(filename), title_style)
ws.write(current_row, 1, "", title_style)
ws.write_merge(...) # 导致单元格覆盖冲突
# 修复后(正确写法)
ws.write_merge(
current_row, current_row, 0, 4,
"文件: {}".format(filename),
title_style
)
```
2. **sheet名称长度限制**:
```python
sheet_name = prefix.rstrip('_') # 去掉末尾下划线
if len(sheet_name) > 31: # Excel sheet名称长度限制
sheet_name = sheet_name[:31] # 截断过长的名称
```
3. **样式优化**:
```python
# 预定义样式避免重复创建
title_style = xlwt.easyxf('font: bold on, height 280; align: wrap on, vert centre')
header_style = xlwt.easyxf('font: bold on')
normal_style = xlwt.easyxf()
```
### 错误原因分析:
1. **原始错误信息**:
```
Exception: Attempt to overwrite cell: sheetname=u'01' rowx=0 colx=0
```
2. **根本原因**:
- 先使用`ws.write()`在(0,0)位置写入了单元格
- 再使用`write_merge()`尝试覆盖同一个位置的单元格
- xlwt库不允许这种覆盖操作,导致异常
3. **解决方案**:
- 直接使用`write_merge()`一次性写入合并单元格
- 避免先写入单个单元格再尝试合并
### 使用说明:
1. 将修复后的脚本保存为`fixed_storage.py`
2. 确保`moscow`和`tacoof`目录存在且包含数据文件
3. 运行命令:
```bash
python fixed_storage.py
```
4. 输出文件`file_prefix_compare.xlsx`将包含:
- 每个数字前缀一个Sheet页(01, 02, 03等)
- 每个文件一个独立表格
- 所有路径和大小对比数据
### 预期输出结构示例:
**Sheet: 01**
```
| 文件: 01_mount.txt | (合并单元格) |
|---------------------------------|-------------|
| 路径 | 机器1大小 | 机器2大小 | 差值 | 差异% |
| /system/bin/mount| 120.0 | 125.0 | 5.0 | 4.17% |
| ... | ... | ... | ... | ... |
```
**Sheet: 02**
```
| 文件: 02_system.txt | (合并单元格) |
|---------------------------------|-------------|
| 路径 | 机器1大小 | 机器2大小 | 差值 | 差异% |
| /system/bin/sh | 80.0 | 80.0 | 0.0 | 0.0% |
| ... | ... | ... | ... | ... |
```