文档证据格式:PDF、DOCX 与 TXT 的博弈

文档证据,说白了就是电子取证里最常碰到的「硬通货」。我经手的案子,十有八九都离不开 PDF、DOCX 或者纯文本文件。这三种格式看着简单,但坑特别多。你想想看,一个 PDF 可能藏着隐藏图层,一个 DOCX 可能带着你根本不知道的修订记录,一个 TXT 文件可能因为编码问题直接变成乱码——这些细节,往往就是案件的关键。

我个人习惯,拿到文档证据的第一件事,不是急着看内容,而是先搞清楚它的「底细」:格式是什么?用什么工具生成的?有没有被篡改的痕迹?今天我们就来聊聊这三种格式的解析、提取和互转实战。

PDF 解析与提取:不只是「另存为」那么简单

PDF 这玩意儿,设计初衷是为了「跨平台一致显示」,而不是为了「方便提取内容」。所以很多新手觉得 PDF 转 Word 就是点一下「另存为」——嗯,我当年也这么干过,直到有一次在法庭上,对方律师指出我提交的 PDF 提取文本里缺了一段关键数字。

为什么会这样?因为 PDF 里的文字可能不是「文字」,而是图形。比如扫描件、图片型 PDF,你根本选不中文字。这时候就得用 OCR(光学字符识别)。我常用的工具有 Adobe Acrobat 的 OCR 引擎,或者开源的 Tesseract。但 OCR 不是万能的,遇到手写体、模糊扫描件,识别率会直线下降。

我的经验: 对于关键证据,永远不要只依赖 OCR 结果。一定要对照原始 PDF 截图,逐字核对。我曾经因为 OCR 把「10000」识别成「1000O」,差点让证据失效。

PDF 提取的另一个难点是「结构化信息」。比如表格、页眉页脚、水印。普通的文本提取工具会把表格内容打乱,让你分不清哪行是哪列。我建议用 pdfplumber(Python 库)来处理表格,它能保留单元格的相对位置。

import pdfplumber

with pdfplumber.open("evidence.pdf") as pdf:
    for page in pdf.pages:
        tables = page.extract_tables()
        for table in tables:
            for row in table:
                print(row)  # 每一行是一个列表,保留单元格顺序

还有一个容易被忽略的点:PDF 的元数据。创建时间、修改时间、作者、甚至使用的软件版本,都可能成为线索。用 exiftool 或者 Python 的 PyPDF2 都能提取。

注意: PDF 的修改时间是可以被篡改的。不要仅凭元数据判断文件生成时间,要结合文件系统的时间戳(比如 NTFS 的 $MFT)交叉验证。

DOCX 结构分析:一个 ZIP 包里的秘密

很多人以为 DOCX 就是一个 Word 文件,其实它本质上是一个 ZIP 压缩包。你把它后缀改成 .zip,解压开,就能看到里面的 XML 文件。这些 XML 文件才是真正的文档内容。

我刚开始做取证时,遇到一个案子:对方声称一份合同是「原始文件」,但我总觉得不对劲。后来我解压了 DOCX,查看了 document.xml,发现里面有一段被隐藏的修订记录——对方删除了关键条款,然后又重新保存。但 DOCX 的修订标记(<w:ins> 和 <w:del>)还留在 XML 里。这就是铁证。

DOCX 的核心结构大致如下:

路径 内容 取证价值
word/document.xml 正文内容(含样式、修订) 高。可提取隐藏文本、修订记录
word/comments.xml 批注 高。批注作者、时间、内容
word/styles.xml 样式定义 中。可辅助判断文档是否被批量修改
docProps/core.xml 元数据(作者、创建时间等) 中。但容易被篡改
word/media/ 嵌入的图片、视频 高。可能包含隐藏的媒体文件

提取 DOCX 中的文本,我推荐用 python-docx 库。它能保留段落、表格、列表的结构,比直接解压 XML 方便得多。

from docx import Document

doc = Document("evidence.docx")
for para in doc.paragraphs:
    print(para.text)  # 逐段输出文本

# 提取表格
for table in doc.tables:
    for row in table.rows:
        for cell in row.cells:
            print(cell.text)
避坑指南: 我曾经遇到一个 DOCX 文件,用 Word 打开显示正常,但用 python-docx 提取时,某些段落是空的。后来发现,那些段落使用了「隐藏文字」属性(字体颜色设为白色,或者勾选了「隐藏」)。在 XML 里,这些段落有 <w:vanish/> 标记。提取时一定要检查这个属性。

纯文本编码问题:UTF-8、GBK 与 ASCII 的混战

纯文本文件看起来最简单,但编码问题能让你怀疑人生。我见过太多新手,拿到一个 TXT 文件,打开一看全是乱码,就以为文件损坏了。其实只是编码不对。

常见的编码有:

  • ASCII: 最古老,只能表示英文字母、数字和少量符号。一个字节一个字符。
  • UTF-8: 现在的主流,兼容 ASCII,能表示全世界所有字符。变长编码(1-4 字节)。
  • GBK: 中文环境下常见,Windows 简体中文系统的默认编码。每个汉字占 2 个字节。
  • GB2312: GBK 的前身,字符集较小。
  • ISO-8859-1: 西欧编码,也叫 Latin-1。

怎么判断一个 TXT 文件的编码?我一般用 chardet 库,它能自动检测编码。

import chardet

with open("unknown.txt", "rb") as f:
    raw = f.read(10000)  # 读取前 10000 字节
    result = chardet.detect(raw)
    print(result)  # 输出:{'encoding': 'GBK', 'confidence': 0.99}

但 chardet 不是 100% 准确。尤其是文件内容很短的时候,它可能猜错。我的习惯是:先看文件来源。如果是从 Windows 中文系统导出的,大概率是 GBK;如果是 Linux 或 macOS 上生成的,大概率是 UTF-8。如果还是不确定,就用十六进制编辑器(比如 HxD)打开,看看字节序列。比如,UTF-8 的 BOM 头是 EF BB BF,GBK 没有 BOM。

重要: 在取证过程中,永远不要直接修改原始文件。如果你需要转换编码,先复制一份副本。我曾经见过有人用记事本打开 GBK 文件,然后「另存为」UTF-8,结果某些特殊字符(比如全角空格、制表符)被错误转换,导致证据内容出现偏差。

格式互转实战:从 PDF 到 DOCX,再到 TXT

格式互转是日常操作,但每个转换步骤都有坑。我画了一张流程图,帮你理清思路。

文档格式互转流程图 文档格式互转核心流程 源文件 格式判断 PDF DOCX TXT PDF 解析/OCR 提取文本+表格 DOCX 解包 提取修订+批注 编码检测 (chardet/十六进制) 统一输出(TXT/CSV) 注意:转换后必须人工核对 关键数据(数字、日期、金额)

具体到实战,我总结了几条经验:

  • PDF → DOCX: 用 Adobe Acrobat 的「导出」功能,或者用 pdf2docx 库。但要注意,复杂排版(比如多栏、文本框)可能会错位。转换后一定要逐页核对。
  • DOCX → PDF: 这个相对简单,Word 自带的「另存为 PDF」就够用。但要注意,如果 DOCX 里有嵌入字体,PDF 里可能显示异常。我建议在转换前,把字体都设为常见字体(如宋体、Arial)。
  • PDF → TXT: 纯文本提取,用 PyPDF2 或 pdfminer。但表格会丢失结构,变成一堆数字堆在一起。如果表格很重要,我建议先转 DOCX,再手动整理。
  • DOCX → TXT: 用 python-docx 提取文本,然后保存为 UTF-8 编码的 TXT。注意:DOCX 里的图片、图表、公式都会丢失。如果证据包含这些,一定要在转换说明里注明。
我的习惯: 每次格式转换,我都会生成一个「转换日志」,记录源文件哈希值、转换工具、转换时间、目标文件哈希值。这样万一有人质疑证据的完整性,我能拿出完整的操作记录。说白了,电子取证不只是技术活,更是程序活——每一步都要经得起推敲。

嗯,文档证据这块,说难不难,说简单也不简单。关键是要有耐心,别放过任何一个细节。你想想看,一个隐藏的修订标记,一个错误的编码,都可能让整个案子翻盘。所以,下次拿到文档证据,别急着看内容——先看看它的「底细」。