88 lines
1.7 KiB
Go
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>")
|
|
}
|