Skip to content

Commit ed7670a

Browse files
committed
feat: 新增文件内容清理
1 parent 0da0e9f commit ed7670a

4 files changed

Lines changed: 425 additions & 8 deletions

File tree

reader.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,26 @@ type Document struct {
2626
Metadata map[string]string
2727
}
2828

29+
// CleanContent 使用默认配置清理文档内容
30+
func (d *Document) CleanContent() {
31+
d.Content = CleanText(d.Content)
32+
}
33+
34+
// CleanContentWith 使用自定义清理器清理文档内容
35+
func (d *Document) CleanContentWith(cleaner *TextCleaner) {
36+
d.Content = cleaner.Clean(d.Content)
37+
}
38+
39+
// CleanContentMinimal 使用最小配置清理文档内容
40+
func (d *Document) CleanContentMinimal() {
41+
d.Content = CleanTextMinimal(d.Content)
42+
}
43+
44+
// CleanContentAggressive 使用激进配置清理文档内容
45+
func (d *Document) CleanContentAggressive() {
46+
d.Content = CleanTextAggressive(d.Content)
47+
}
48+
2949
// GetSupportedFormats 返回当前支持的文档格式列表
3050
func GetSupportedFormats() []string {
3151
formats := make([]string, len(supportedFormats))
@@ -90,3 +110,23 @@ func ReadDocument(filePath string) (*Document, error) {
90110
Metadata: metadata,
91111
}, nil
92112
}
113+
114+
// ReadDocumentWithClean 读取文档并自动应用默认清理
115+
func ReadDocumentWithClean(filePath string) (*Document, error) {
116+
doc, err := ReadDocument(filePath)
117+
if err != nil {
118+
return nil, err
119+
}
120+
doc.CleanContent()
121+
return doc, nil
122+
}
123+
124+
// ReadDocumentWithCleanConfig 读取文档并应用自定义清理配置
125+
func ReadDocumentWithCleanConfig(filePath string, cleaner *TextCleaner) (*Document, error) {
126+
doc, err := ReadDocument(filePath)
127+
if err != nil {
128+
return nil, err
129+
}
130+
doc.CleanContentWith(cleaner)
131+
return doc, nil
132+
}

reader_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func TestErrorHandling(t *testing.T) {
7171
}
7272
}
7373

74-
// TestDocxReaderWithRealFile 测试 DOCX 读取器(真实文件)
74+
// TestDocxReaderWithRealFile 测试 DOCX 读取器
7575
func TestDocxReaderWithRealFile(t *testing.T) {
7676
testFile := filepath.Join("testdata", "test.docx")
7777
if _, err := os.Stat(testFile); err != nil {
@@ -104,7 +104,7 @@ func TestDocxReaderWithRealFile(t *testing.T) {
104104
t.Logf("元数据: %+v", metadata)
105105
}
106106

107-
// TestPdfReaderWithRealFile 测试 PDF 读取器(真实文件)
107+
// TestPdfReaderWithRealFile 测试 PDF 读取器
108108
func TestPdfReaderWithRealFile(t *testing.T) {
109109
testFile := filepath.Join("testdata", "test.pdf")
110110
if _, err := os.Stat(testFile); err != nil {
@@ -140,7 +140,7 @@ func TestPdfReaderWithRealFile(t *testing.T) {
140140
t.Logf("元数据: %+v", metadata)
141141
}
142142

143-
// TestXlsxReaderWithRealFile 测试 XLSX 读取器(真实文件)
143+
// TestXlsxReaderWithRealFile 测试 XLSX 读取器
144144
func TestXlsxReaderWithRealFile(t *testing.T) {
145145
testFile := filepath.Join("testdata", "test.xlsx")
146146
if _, err := os.Stat(testFile); err != nil {
@@ -186,7 +186,7 @@ func TestXlsxReaderWithRealFile(t *testing.T) {
186186
t.Logf("元数据: %+v", metadata)
187187
}
188188

189-
// TestPptxReaderWithRealFile 测试 PPTX 读取器(真实文件)
189+
// TestPptxReaderWithRealFile 测试 PPTX 读取器
190190
func TestPptxReaderWithRealFile(t *testing.T) {
191191
testFile := filepath.Join("testdata", "test.pptx")
192192
if _, err := os.Stat(testFile); err != nil {
@@ -226,7 +226,7 @@ func TestPptxReaderWithRealFile(t *testing.T) {
226226
t.Logf("元数据: %+v", metadata)
227227
}
228228

229-
// TestTxtReaderWithRealFile 测试 TXT 读取器(真实文件)
229+
// TestTxtReaderWithRealFile 测试 TXT 读取器
230230
func TestTxtReaderWithRealFile(t *testing.T) {
231231
testFile := filepath.Join("testdata", "test.txt")
232232
if _, err := os.Stat(testFile); err != nil {
@@ -260,7 +260,7 @@ func TestTxtReaderWithRealFile(t *testing.T) {
260260
t.Logf("元数据: %+v", metadata)
261261
}
262262

263-
// TestCsvReaderWithRealFile 测试 CSV 读取器(真实文件)
263+
// TestCsvReaderWithRealFile 测试 CSV 读取器
264264
func TestCsvReaderWithRealFile(t *testing.T) {
265265
testFile := filepath.Join("testdata", "test.csv")
266266
if _, err := os.Stat(testFile); err != nil {
@@ -303,7 +303,7 @@ func TestCsvReaderWithRealFile(t *testing.T) {
303303
t.Logf("元数据: %+v", metadata)
304304
}
305305

306-
// TestMdReaderWithRealFile 测试 Markdown 读取器(真实文件)
306+
// TestMdReaderWithRealFile 测试 Markdown 读取器
307307
func TestMdReaderWithRealFile(t *testing.T) {
308308
testFile := filepath.Join("testdata", "test.md")
309309
if _, err := os.Stat(testFile); err != nil {
@@ -337,7 +337,7 @@ func TestMdReaderWithRealFile(t *testing.T) {
337337
t.Logf("元数据: %+v", metadata)
338338
}
339339

340-
// TestRtfReaderWithRealFile 测试 RTF 读取器(真实文件)
340+
// TestRtfReaderWithRealFile 测试 RTF 读取器
341341
func TestRtfReaderWithRealFile(t *testing.T) {
342342
testFile := filepath.Join("testdata", "test.rtf")
343343
if _, err := os.Stat(testFile); err != nil {

textcleaner.go

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package docreader
2+
3+
import (
4+
"regexp"
5+
"strings"
6+
"unicode"
7+
)
8+
9+
// TextCleaner 提供文本清理功能,用于优化大模型理解
10+
type TextCleaner struct {
11+
// 是否移除多余空行(连续的空行压缩为一个)
12+
RemoveExtraBlankLines bool
13+
// 是否移除行首行尾空格
14+
TrimSpaces bool
15+
// 是否移除多余空格(连续空格压缩为一个)
16+
RemoveExtraSpaces bool
17+
// 是否移除特殊控制字符
18+
RemoveControlChars bool
19+
// 最大连续空行数(0表示不限制)
20+
MaxConsecutiveBlankLines int
21+
}
22+
23+
// DefaultTextCleaner 返回默认配置的文本清理器
24+
func DefaultTextCleaner() *TextCleaner {
25+
return &TextCleaner{
26+
RemoveExtraBlankLines: true,
27+
TrimSpaces: true,
28+
RemoveExtraSpaces: true,
29+
RemoveControlChars: true,
30+
MaxConsecutiveBlankLines: 1,
31+
}
32+
}
33+
34+
// Clean 清理文本内容
35+
func (tc *TextCleaner) Clean(text string) string {
36+
if text == "" {
37+
return ""
38+
}
39+
40+
// 1. 移除控制字符(保留换行符、制表符等有意义的空白字符)
41+
if tc.RemoveControlChars {
42+
text = tc.removeControlChars(text)
43+
}
44+
45+
// 2. 统一换行符为 \n
46+
text = normalizeLineBreaks(text)
47+
48+
// 3. 按行处理
49+
lines := strings.Split(text, "\n")
50+
var cleanedLines []string
51+
consecutiveBlankLines := 0
52+
53+
for _, line := range lines {
54+
// 移除行首行尾空格
55+
if tc.TrimSpaces {
56+
line = strings.TrimSpace(line)
57+
}
58+
59+
// 移除多余空格
60+
if tc.RemoveExtraSpaces && line != "" {
61+
line = tc.removeExtraSpaces(line)
62+
}
63+
64+
// 处理空行
65+
if line == "" {
66+
consecutiveBlankLines++
67+
68+
// 如果 MaxConsecutiveBlankLines 为 0,移除所有空行
69+
if tc.MaxConsecutiveBlankLines == 0 {
70+
continue
71+
}
72+
73+
// 如果设置了最大连续空行数(大于0)
74+
if tc.MaxConsecutiveBlankLines > 0 && consecutiveBlankLines > tc.MaxConsecutiveBlankLines {
75+
continue // 跳过多余的空行
76+
}
77+
78+
// 如果要移除多余空行(且 MaxConsecutiveBlankLines < 0 表示不限制)
79+
if tc.RemoveExtraBlankLines && consecutiveBlankLines > 1 && tc.MaxConsecutiveBlankLines != -1 {
80+
continue // 跳过多余的空行
81+
}
82+
} else {
83+
consecutiveBlankLines = 0
84+
}
85+
86+
cleanedLines = append(cleanedLines, line)
87+
}
88+
89+
// 4. 移除开头和结尾的空行
90+
cleanedLines = trimEmptyLines(cleanedLines)
91+
92+
// 5. 合并结果
93+
result := strings.Join(cleanedLines, "\n")
94+
95+
return result
96+
}
97+
98+
// removeControlChars 移除控制字符,保留必要的空白字符
99+
func (tc *TextCleaner) removeControlChars(text string) string {
100+
var builder strings.Builder
101+
builder.Grow(len(text))
102+
103+
for _, r := range text {
104+
// 保留可打印字符、换行符、制表符、回车符
105+
if unicode.IsPrint(r) || r == '\n' || r == '\t' || r == '\r' {
106+
builder.WriteRune(r)
107+
}
108+
}
109+
110+
return builder.String()
111+
}
112+
113+
// removeExtraSpaces 移除多余的空格(连续空格压缩为一个)
114+
func (tc *TextCleaner) removeExtraSpaces(text string) string {
115+
// 使用正则表达式将多个连续空格替换为单个空格
116+
re := regexp.MustCompile(`[ \t]+`)
117+
return re.ReplaceAllString(text, " ")
118+
}
119+
120+
// normalizeLineBreaks 统一换行符
121+
func normalizeLineBreaks(text string) string {
122+
// 将 \r\n 和 \r 统一替换为 \n
123+
text = strings.ReplaceAll(text, "\r\n", "\n")
124+
text = strings.ReplaceAll(text, "\r", "\n")
125+
return text
126+
}
127+
128+
// trimEmptyLines 移除开头和结尾的空行
129+
func trimEmptyLines(lines []string) []string {
130+
// 移除开头的空行
131+
start := 0
132+
for start < len(lines) && lines[start] == "" {
133+
start++
134+
}
135+
136+
// 移除结尾的空行
137+
end := len(lines)
138+
for end > start && lines[end-1] == "" {
139+
end--
140+
}
141+
142+
if start >= end {
143+
return []string{}
144+
}
145+
146+
return lines[start:end]
147+
}
148+
149+
// CleanText 使用默认配置清理文本的便捷函数
150+
func CleanText(text string) string {
151+
cleaner := DefaultTextCleaner()
152+
return cleaner.Clean(text)
153+
}
154+
155+
// CleanTextMinimal 使用最小清理配置(仅清理基本的空白)
156+
func CleanTextMinimal(text string) string {
157+
cleaner := &TextCleaner{
158+
RemoveExtraBlankLines: false,
159+
TrimSpaces: true,
160+
RemoveExtraSpaces: true,
161+
RemoveControlChars: true,
162+
MaxConsecutiveBlankLines: -1, // 不限制空行数
163+
}
164+
return cleaner.Clean(text)
165+
}
166+
167+
// CleanTextAggressive 使用激进的清理配置(最大程度压缩空间)
168+
func CleanTextAggressive(text string) string {
169+
cleaner := &TextCleaner{
170+
RemoveExtraBlankLines: true,
171+
TrimSpaces: true,
172+
RemoveExtraSpaces: true,
173+
RemoveControlChars: true,
174+
MaxConsecutiveBlankLines: 0, // 不允许空行
175+
}
176+
return cleaner.Clean(text)
177+
}

0 commit comments

Comments
 (0)