Python解析大PDF传Go时cmd.Output()报错求助
问题根源分析
- 运行时恐慌直接原因:Go代码中
WriteString的错误处理块误用了未定义的变量err2,这会触发invalid memory address类型的运行时恐慌。 - 大PDF处理失败原因:
cmd.Output()会将子进程的所有标准输出一次性加载到内存,超大文本会导致内存占用过高;同时管道缓冲区存在大小限制,子进程可能因无法继续写入stdout而阻塞退出,返回exit status 1。
修复与优化方案
先修复基础错误的Go代码
解决恐慌问题并完善错误处理:
func parsePdf(path string) string { cmd := exec.Command("python", "pdf_parser.py", path) output, err := cmd.Output() if err != nil { fmt.Println("执行Python脚本出错:", err) return "" } f, err := os.Create("go-pdf-output.txt") if err != nil { fmt.Println("创建文件出错:", err) return string(output) } defer f.Close() // 确保文件资源被释放 _, err = f.WriteString(string(output)) if err != nil { fmt.Println("写入文件出错:", err) } return string(output) }
针对大PDF的优化方案
方案一:流式读取输出(推荐)
通过管道实时读取子进程输出并写入文件,避免一次性加载全部文本到内存,彻底解决管道缓冲区和内存过载问题:
import ( "fmt" "io" "os" "os/exec" ) func parsePdf(path string) error { cmd := exec.Command("python", "pdf_parser.py", path) // 创建stdout管道 stdout, err := cmd.StdoutPipe() if err != nil { return fmt.Errorf("创建stdout管道失败: %w", err) } // 启动子进程 if err := cmd.Start(); err != nil { return fmt.Errorf("启动Python脚本失败: %w", err) } // 创建输出文件 f, err := os.Create("go-pdf-output.txt") if err != nil { return fmt.Errorf("创建输出文件失败: %w", err) } defer f.Close() // 流式复制输出到文件 _, err = io.Copy(f, stdout) if err != nil { return fmt.Errorf("读取输出失败: %w", err) } // 等待子进程执行完成 if err := cmd.Wait(); err != nil { return fmt.Errorf("Python脚本执行失败: %w", err) } return nil }
方案二:Python直接写入文件,Go读取结果
让Python脚本直接将解析后的文本写入文件,绕过进程间管道传递的限制:
修改Python脚本:
import fitz import sys path = sys.argv[1] output_path = "pdf-output.txt" # 可通过命令行参数动态指定路径 doc = fitz.open(path) with open(output_path, "w", encoding="utf-8") as f: for page in doc: text = page.get_text("text") f.write(text + " ")
修改Go代码:
import ( "fmt" "os" "os/exec" ) func parsePdf(path string) error { cmd := exec.Command("python", "pdf_parser.py", path) err := cmd.Run() if err != nil { return fmt.Errorf("执行Python脚本失败: %w", err) } // 读取Python生成的文件 content, err := os.ReadFile("pdf-output.txt") if err != nil { return fmt.Errorf("读取输出文件失败: %w", err) } // 写入目标文件 f, err := os.Create("go-pdf-output.txt") if err != nil { return fmt.Errorf("创建目标文件失败: %w", err) } defer f.Close() _, err = f.Write(content) return err }
额外建议
- 处理超大文件时,优先采用流式处理逻辑,避免在内存中存储完整文本。
- 给Python脚本添加错误处理(如文件不存在、PDF损坏等),并将错误信息输出到stderr,Go可通过
cmd.StderrPipe()捕获错误信息用于调试。
内容的提问来源于stack exchange,提问作者Harsh Vardhan
相关产品推荐
相关产品推荐

