diff --git a/excel_to_markdown.py b/excel_to_markdown.py new file mode 100644 index 0000000..768532c --- /dev/null +++ b/excel_to_markdown.py @@ -0,0 +1,350 @@ +""" +Excel to Markdown Converter + +将Excel文件转换为Markdown格式的表格,包含行号和列号(英文字母)。 +""" + +import pandas as pd +import argparse +import sys +from pathlib import Path + + +def number_to_excel_col(n): + """将数字转换为Excel列号(A, B, ..., Z, AA, AB, ...)""" + result = "" + while n > 0: + n -= 1 + result = chr(n % 26 + ord('A')) + result + n //= 26 + return result + + +def excel_to_markdown(input_file, output_file=None, sheet_name=0, include_row_numbers=True, include_col_numbers=True): + """ + 将Excel文件转换为Markdown表格 + + Args: + input_file: 输入的Excel文件路径 + output_file: 输出的Markdown文件路径(可选,默认为同名.md文件) + sheet_name: 工作表名称或索引,或列表(默认为第一个工作表) + include_row_numbers: 是否包含行号 + include_col_numbers: 是否包含列号 + """ + input_path = Path(input_file) + + if not input_path.exists(): + print(f"错误: 文件 '{input_file}' 不存在") + return False + + # 设置默认输出文件名 + if output_file is None: + output_file = input_path.with_suffix('.md') + else: + output_file = Path(output_file) + + try: + # 读取Excel文件 + print(f"正在读取文件: {input_file}") + + # 获取所有工作表名称(用于索引转换) + xls = pd.ExcelFile(input_file) + all_sheet_names = xls.sheet_names + + # 将索引转换为实际的工作表名称 + def resolve_sheet_name(name): + if isinstance(name, int): + if 0 <= name < len(all_sheet_names): + return all_sheet_names[name] + else: + raise ValueError(f"工作表索引 {name} 超出范围") + return name + + # 处理多个工作表的情况 + if isinstance(sheet_name, (list, tuple)): + # 转换索引为名称 + resolved_names = [resolve_sheet_name(s) for s in sheet_name] + dfs = pd.read_excel(input_file, sheet_name=resolved_names, header=None) + # 如果只有一个工作表,转换为单个DataFrame + if len(resolved_names) == 1: + dfs = {resolved_names[0]: dfs} + sheet_name = resolved_names + else: + resolved_name = resolve_sheet_name(sheet_name) + df = pd.read_excel(input_file, sheet_name=resolved_name, header=None) + dfs = {resolved_name: df} + sheet_name = resolved_name + + # 生成所有工作表的Markdown内容 + all_sheets_content = {} + + for sheet_key, df in dfs.items(): + # 转换数据为字符串,处理NaN值 + df = df.fillna('') + df = df.astype(str) + + # 生成Markdown表格 + markdown_lines = [] + + # 添加工作表标题 + sheet_title = f"工作表: {sheet_key}" + markdown_lines.append(f"## {sheet_title}\n") + + # 添加列号行 + if include_col_numbers: + col_headers = [''] if include_row_numbers else [] + col_headers.extend(number_to_excel_col(i + 1) for i in range(len(df.columns))) + markdown_lines.append('| ' + ' | '.join(col_headers) + ' |') + markdown_lines.append('| ' + ' | '.join(['---' for _ in col_headers]) + ' |') + + # 添加数据行 + for idx, row in df.iterrows(): + row_data = [] + if include_row_numbers: + row_data.append(str(idx + 1)) + row_data.extend(row) + markdown_lines.append('| ' + ' | '.join(row_data) + ' |') + + # 添加统计信息 + markdown_lines.append(f"\n**统计信息:**") + markdown_lines.append(f"- 总行数: {len(df)}") + markdown_lines.append(f"- 总列数: {len(df.columns)}") + markdown_lines.append(f"- 数据范围: A1:{number_to_excel_col(len(df.columns))}{len(df)}") + + all_sheets_content[sheet_key] = '\n'.join(markdown_lines) + + # 写入文件 + if len(dfs) == 1: + # 单个工作表:直接写入 + sheet_key = list(dfs.keys())[0] + output_content = f"# {input_path.stem}\n\n" + output_content += f"从 `{input_file}` (工作表: {sheet_key}) 转换\n\n" + output_content += all_sheets_content[sheet_key] + output_file.write_text(output_content, encoding='utf-8') + + print(f"✓ 转换成功!") + print(f" 输入文件: {input_file}") + print(f" 输出文件: {output_file}") + print(f" 工作表: {sheet_key}") + print(f" 行数: {len(dfs[sheet_key])}, 列数: {len(dfs[sheet_key].columns)}") + else: + return all_sheets_content, dfs, input_path, input_file + + except Exception as e: + print(f"错误: {str(e)}") + import traceback + traceback.print_exc() + return False + + +def convert_multiple_sheets(input_file, output_file=None, sheet_names=None, include_row_numbers=True, include_col_numbers=True, merge_to_one_file=True): + """ + 转换多个工作表 + + Args: + input_file: 输入的Excel文件路径 + output_file: 输出的Markdown文件路径 + sheet_names: 工作表名称或索引列表 + include_row_numbers: 是否包含行号 + include_col_numbers: 是否包含列号 + merge_to_one_file: 是否合并到一个文件(True)或分别输出(False) + """ + input_path = Path(input_file) + + if not input_path.exists(): + print(f"错误: 文件 '{input_file}' 不存在") + return False + + # 设置默认输出文件名 + if output_file is None: + output_file = input_path.with_suffix('.md') + else: + output_file = Path(output_file) + + try: + # 读取Excel文件 + print(f"正在读取文件: {input_file}") + + # 获取所有工作表名称 + xls = pd.ExcelFile(input_file) + all_sheet_names = xls.sheet_names + + if sheet_names is None or len(sheet_names) == 0: + # 读取所有工作表 + sheet_names = all_sheet_names + print(f"找到 {len(sheet_names)} 个工作表") + else: + # 将索引转换为实际的工作表名称 + resolved_names = [] + for name in sheet_names: + if isinstance(name, int): + # 索引转换为名称 + if 0 <= name < len(all_sheet_names): + resolved_names.append(all_sheet_names[name]) + else: + print(f"警告: 工作表索引 {name} 超出范围,已跳过") + else: + # 直接使用名称 + resolved_names.append(name) + sheet_names = resolved_names + + # 使用实际的工作表名称读取数据 + dfs = pd.read_excel(input_file, sheet_name=sheet_names, header=None) + + # 确保返回的是字典格式 + if not isinstance(dfs, dict): + dfs = {sheet_names[0]: dfs} + + # 生成所有工作表的Markdown内容 + all_sheets_content = {} + + for sheet_key, df in dfs.items(): + # 转换数据为字符串,处理NaN值 + df = df.fillna('') + df = df.astype(str) + + # 生成Markdown表格 + markdown_lines = [] + + # 添加工作表标题 + sheet_title = f"工作表: {sheet_key}" + markdown_lines.append(f"## {sheet_title}\n") + + # 添加列号行 + if include_col_numbers: + col_headers = [''] if include_row_numbers else [] + col_headers.extend(number_to_excel_col(i + 1) for i in range(len(df.columns))) + markdown_lines.append('| ' + ' | '.join(col_headers) + ' |') + markdown_lines.append('| ' + ' | '.join(['---' for _ in col_headers]) + ' |') + + # 添加数据行 + for idx, row in df.iterrows(): + row_data = [] + if include_row_numbers: + row_data.append(str(idx + 1)) + row_data.extend(row) + markdown_lines.append('| ' + ' | '.join(row_data) + ' |') + + # 添加统计信息 + markdown_lines.append(f"\n**统计信息:**") + markdown_lines.append(f"- 总行数: {len(df)}") + markdown_lines.append(f"- 总列数: {len(df.columns)}") + markdown_lines.append(f"- 数据范围: A1:{number_to_excel_col(len(df.columns))}{len(df)}") + + all_sheets_content[sheet_key] = '\n'.join(markdown_lines) + + # 写入文件 + if merge_to_one_file: + # 合并到一个文件 + output_content = f"# {input_path.stem}\n\n" + output_content += f"从 `{input_file}` 转换,共 {len(dfs)} 个工作表\n\n" + output_content += "---\n\n" + + for sheet_key in sheet_names: + if sheet_key in all_sheets_content: + output_content += all_sheets_content[sheet_key] + "\n\n---\n\n" + + output_file.write_text(output_content, encoding='utf-8') + + print(f"✓ 转换成功!") + print(f" 输入文件: {input_file}") + print(f" 输出文件: {output_file}") + print(f" 工作表数量: {len(dfs)}") + for sheet_key in sheet_names: + if sheet_key in dfs: + print(f" - {sheet_key}: {len(dfs[sheet_key])}行 x {len(dfs[sheet_key].columns)}列") + else: + # 分别输出到多个文件 + output_stem = output_file.stem + output_suffix = output_file.suffix + output_dir = output_file.parent + + for sheet_key in sheet_names: + if sheet_key not in all_sheets_content: + continue + + # 为每个工作表创建单独的文件 + sheet_filename = f"{output_stem}_{sheet_key}{output_suffix}" + sheet_output_file = output_dir / sheet_filename + + output_content = f"# {input_path.stem} - {sheet_key}\n\n" + output_content += f"从 `{input_file}` (工作表: {sheet_key}) 转换\n\n" + output_content += all_sheets_content[sheet_key] + + sheet_output_file.write_text(output_content, encoding='utf-8') + + print(f"✓ 转换成功!") + print(f" 输入文件: {input_file}") + print(f" 输出目录: {output_dir}") + print(f" 工作表数量: {len(dfs)}") + for sheet_key in sheet_names: + if sheet_key in dfs: + sheet_filename = f"{output_stem}_{sheet_key}{output_suffix}" + print(f" - {sheet_key} -> {sheet_filename} ({len(dfs[sheet_key])}行 x {len(dfs[sheet_key].columns)}列)") + + return True + + except Exception as e: + print(f"错误: {str(e)}") + import traceback + traceback.print_exc() + return False + + +# ============================================================ +# 配置区域 - 直接修改下面的参数 +# ============================================================ + +# 输入文件路径(必填) +INPUT_FILE = r"D:\python\playwrite\data\YTHN-100.A0.532-BOM-1.2版.xlsx" + +# 输出文件路径(可选,默认为输入文件同名.md文件) +OUTPUT_FILE = None # 或指定 r"D:\path\to\output.md" + +# 工作表名称或索引(可选,默认为0即第一个工作表) +# 可以是单个值: 0 或 "Sheet1" +# 可以是列表: [0, 1, 2] 或 ["Sheet1", "Sheet2", "Sheet3"] +# 可以是 None 表示读取所有工作表 +SHEET_NAMES = [0, 1,2,3] # 指定多个工作表索引 + +# 是否包含行号(可选,默认为True) +INCLUDE_ROW_NUMBERS = True + +# 是否包含列号(可选,默认为True) +INCLUDE_COL_NUMBERS = True + +# 如果指定了多个工作表,是否合并到一个文件(可选,默认为True) +# True: 所有工作表合并到一个文件 +# False: 每个工作表生成单独的文件(文件名格式: 原文件名_工作表名.md) +MULTI_SHEETS_TO_ONE_FILE = True + +# ============================================================ + + +def main(): + """主函数 - 使用上方配置区域定义的参数""" + # 判断是否为多个工作表 + if isinstance(SHEET_NAMES, list) and len(SHEET_NAMES) > 1: + # 多个工作表 + convert_multiple_sheets( + input_file=INPUT_FILE, + output_file=OUTPUT_FILE, + sheet_names=SHEET_NAMES, + include_row_numbers=INCLUDE_ROW_NUMBERS, + include_col_numbers=INCLUDE_COL_NUMBERS, + merge_to_one_file=MULTI_SHEETS_TO_ONE_FILE + ) + else: + # 单个工作表 + sheet_name = SHEET_NAMES if isinstance(SHEET_NAMES, list) and len(SHEET_NAMES) == 1 else SHEET_NAMES + excel_to_markdown( + input_file=INPUT_FILE, + output_file=OUTPUT_FILE, + sheet_name=sheet_name, + include_row_numbers=INCLUDE_ROW_NUMBERS, + include_col_numbers=INCLUDE_COL_NUMBERS + ) + + +if __name__ == '__main__': + main()