From 40b55b3c326f954ca36171c6ed95a016898ef244 Mon Sep 17 00:00:00 2001 From: Shuming Liu Date: Tue, 31 Mar 2026 11:10:40 +0800 Subject: [PATCH] save a version --- server/file_association.py | 138 ++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 34 deletions(-) diff --git a/server/file_association.py b/server/file_association.py index 9ca0e5c..96c0acb 100644 --- a/server/file_association.py +++ b/server/file_association.py @@ -62,11 +62,19 @@ def extract_all_zips( def read_word_content(doc_path: Path) -> List[str]: - """读取 Word 文档内容,提取文本行""" + """读取 Word 文档内容 (.docx 或 .doc),提取文本行""" + if doc_path.suffix.lower() == '.docx': + return read_docx_content(doc_path) + elif doc_path.suffix.lower() == '.doc': + return read_doc_content(doc_path) + return [] + + +def read_docx_content(doc_path: Path) -> List[str]: + """读取 .docx 格式 Word 文档内容""" lines = [] try: - import zipfile as zf - with zf.ZipFile(doc_path, 'r') as z: + with zipfile.ZipFile(doc_path, 'r') as z: content = z.read('word/document.xml').decode('utf-8') texts = re.findall(r']*>(.*?)', content, re.DOTALL) for text in texts: @@ -76,33 +84,87 @@ def read_word_content(doc_path: Path) -> List[str]: lines.append(text) return lines except Exception as e: - print(f"读取 Word 失败:{e}") + print(f"读取 .docx 失败 ({doc_path.name}): {e}") return [] -def find_word_doc(base_dir: Path) -> Optional[Path]: - """自动查找 base_dir 下的 Word 文档 (.doc 或 .docx)""" - for f in base_dir.iterdir(): - if f.is_file() and f.suffix.lower() in ['.doc', '.docx']: - return f +def read_doc_content(doc_path: Path) -> List[str]: + """读取 .doc 格式 Word 文档内容""" + lines = [] + + # 方法 1: 尝试使用 antiword + try: + import subprocess + result = subprocess.run( + ['antiword', str(doc_path)], + capture_output=True, + text=True, + timeout=30 + ) + if result.returncode == 0: + lines = [line.strip() for line in result.stdout.split('\n') if line.strip()] + return lines + except FileNotFoundError: + pass + except Exception as e: + print(f"antiword 读取失败:{e}") + + # 方法 2: 尝试使用 textract + try: + import subprocess + result = subprocess.run( + ['textract', str(doc_path), '--output', '/dev/stdout'], + capture_output=True, + text=True, + timeout=60 + ) + if result.returncode == 0: + lines = [line.strip() for line in result.stdout.split('\n') if line.strip()] + return lines + except FileNotFoundError: + pass + except Exception as e: + print(f"textract 读取失败:{e}") + + # 方法 3: 尝试使用 libreoffice + try: + import subprocess + import tempfile + with tempfile.TemporaryDirectory() as tmpdir: + result = subprocess.run( + ['libreoffice', '--headless', '--convert-to', 'txt', '--outdir', tmpdir, str(doc_path)], + capture_output=True, + text=True, + timeout=30 + ) + if result.returncode == 0: + txt_file = Path(tmpdir) / (doc_path.stem + '.txt') + if txt_file.exists(): + with open(txt_file, 'r', encoding='utf-8', errors='ignore') as f: + lines = [line.strip() for line in f.readlines() if line.strip()] + return lines + except FileNotFoundError: + pass + except Exception as e: + print(f"libreoffice 读取失败:{e}") + + print(f"⚠️ 无法读取 .doc 文件 ({doc_path.name}),请安装以下工具之一:") + print(f" - antiword: brew install antiword") + print(f" - textract: pip install textract") + print(f" - LibreOffice: brew install --cask libreoffice") + return [] + + +def find_word_doc_recursive(base_dir: Path) -> Optional[Path]: + """递归查找 base_dir 下任意层级的 Word 文档 (.docx 优先)""" + # 先找 .docx 文件 + for item in base_dir.rglob("*.docx"): + return item + # 再找 .doc 文件 + for item in base_dir.rglob("*.doc"): + return item return None - -def find_word_content(base_dir: Path) -> Optional[List[str]]: - """自动查找 base_dir 下的 Word 文档并读取内容""" - word_doc = find_word_doc(base_dir) - if word_doc: - return read_word_content(word_doc) - return None - - -def get_word_content(base_dir: Optional[Path] = None) -> List[str]: - """自动查找 Word 文档并读取内容,找到即返回""" - if base_dir is None: - base_dir = Path("data") - return find_word_content(base_dir) or [] - - def create_config_file(word_content: List[str], config_file: Optional[Path] = None) -> Path: """将 Word 内容保存为配置文件""" if config_file is None: @@ -277,15 +339,10 @@ def main() -> None: parser = argparse.ArgumentParser(description='文件关联处理程序') parser.add_argument('--unpack', action='store_true', help='解压文件') parser.add_argument('--associate', action='store_true', help='建立文件关联') - parser.add_argument('--base-dir', type=str, help='基础目录') + parser.add_argument('--base-dir', type=str, help='基础目录(包含 Word 文档的目录)') args = parser.parse_args() - if args.base_dir: - base_dir = Path(args.base_dir) - else: - base_dir = Path("data") - # 解压文件 if args.unpack: extract_all_zips("data") @@ -293,11 +350,24 @@ def main() -> None: # 建立文件关联 if args.associate: - word_content = get_word_content(base_dir) - if not word_content: - print("⚠️ 未找到 Word 文档,请先解压文件") + base_dir = Path(args.base_dir) if args.base_dir else Path("data") + # 递归查找 Word 文档 + word_doc = find_word_doc_recursive(base_dir) + if not word_doc: + print("⚠️ 未找到 Word 文档 (.doc 或 .docx),请先解压文件") return + # 读取 Word 内容 + word_content = read_word_content(word_doc) + if not word_content: + print("⚠️ 无法读取 Word 文档内容") + return + + # 确定关联目录(Word 文档的父目录,包含学科网和 21 世纪教育网) + base_dir = word_doc.parent + print(f"✓ 找到 Word 文档:{word_doc}") + print(f" 关联目录:{base_dir}") + twole_folder = base_dir / "21世纪教育网" zxxk_folder = base_dir / "学科网"