Files
2026-08-05 10:28:44 +08:00

88 lines
1.7 KiB
Go

package common
import (
"archive/zip"
"io"
"strings"
)
type docxParser struct{}
// Parse docx:解 zip 读 word/document.xml,按 <w:p> 段落提取文本
func (p *docxParser) Parse(path string) (string, error) {
r, err := zip.OpenReader(path)
if err != nil {
return "", err
}
defer r.Close()
var sb strings.Builder
for _, f := range r.File {
if f.Name != "word/document.xml" {
continue
}
rc, err := f.Open()
if err != nil {
return "", err
}
body, err := io.ReadAll(rc)
rc.Close()
if err != nil {
return "", err
}
extractDocxText(string(body), &sb)
return sb.String(), nil
}
return "", nil
}
// extractDocxText 解析 document.xml:每个 <w:p> 一段,段内文本去 XML 标签
func extractDocxText(xml string, sb *strings.Builder) {
for {
start := strings.Index(xml, "<w:p")
if start < 0 {
break
}
end := findTagEnd(xml, start)
if end < 0 {
break
}
para := xml[start:end]
xml = xml[end:]
var paraSb strings.Builder
texts := strings.Split(para, "<w:t")
for i, seg := range texts {
if i == 0 {
continue
}
if !strings.HasPrefix(seg, ">") {
continue
}
value := seg[1:]
if j := strings.Index(value, "</w:t>"); j >= 0 {
value = value[:j]
}
paraSb.WriteString(value)
}
line := strings.TrimSpace(paraSb.String())
if line != "" {
sb.WriteString(line)
sb.WriteString("\n")
}
}
}
// findTagEnd 找到 <w:p ...> 对应的 </w:p> 结束位置(返回开标签之后的位置)
func findTagEnd(xml string, start int) int {
openEnd := strings.Index(xml[start:], ">")
if openEnd < 0 {
return -1
}
closeTag := strings.Index(xml[start:], "</w:p>")
if closeTag < 0 {
return -1
}
return start + closeTag + len("</w:p>")
}