Skip to content

Commit 2dbed57

Browse files
committed
feat: 新增可配置的读取方式
1 parent ed7670a commit 2dbed57

14 files changed

Lines changed: 1322 additions & 345 deletions

.DS_Store

6 KB
Binary file not shown.

README.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,149 @@ if err != nil {
214214
fmt.Println(doc.Content)
215215
```
216216

217+
## 高级配置
218+
219+
### 精确控制读取内容
220+
221+
DocReader 提供了强大的配置系统,允许你精确控制要读取的页面和行。
222+
223+
#### 基本配置示例
224+
225+
```go
226+
// 示例 1: 读取指定页码
227+
config := docreader.NewReadConfig().
228+
WithPages(0, 2, 4) // 读取第 0、2、4 页
229+
230+
result, err := docreader.ReadDocumentWithConfig("document.pdf", config)
231+
232+
// 示例 2: 读取页码范围
233+
config := docreader.NewReadConfig().
234+
WithPageRange(0, 5) // 读取第 0-5 页
235+
236+
// 示例 3: 读取指定行
237+
config := docreader.NewReadConfig().
238+
WithLines(0, 5, 10) // 每页只读取第 0、5、10 行
239+
240+
// 示例 4: 读取行范围
241+
config := docreader.NewReadConfig().
242+
WithLineRange(5, 15) // 每页只读取第 5-15 行
243+
244+
// 示例 5: 组合多个范围
245+
config := docreader.NewReadConfig().
246+
WithPageRange(0, 2).
247+
WithPageRange(5, 7). // 读取第 0-2 页和第 5-7 页
248+
WithLineRange(0, 10) // 每页读取第 0-10 行
249+
```
250+
251+
#### 为不同页面设置不同的行配置
252+
253+
```go
254+
// 为特定页面配置不同的行
255+
config := docreader.NewReadConfig().
256+
AddPageLines(0, 1, 3, 5). // 第 0 页:读取第 1、3、5 行
257+
AddPageLineRange(1, 0, 10). // 第 1 页:读取第 0-10 行
258+
AddPageConfig(2, []int{2, 4}, [][2]int{{6, 8}}) // 第 2 页:读取第 2、4 行和第 6-8 行
259+
260+
result, err := docreader.ReadDocumentWithConfig("document.pdf", config)
261+
```
262+
263+
#### XLSX 工作表筛选
264+
265+
```go
266+
// 只读取指定的工作表
267+
config := docreader.NewReadConfig().
268+
WithSheetNames("Sheet1", "Summary")
269+
270+
result, err := docreader.ReadDocumentWithConfig("spreadsheet.xlsx", config)
271+
```
272+
273+
#### 处理结构化结果
274+
275+
```go
276+
result, err := docreader.ReadDocumentWithConfig("document.pdf", config)
277+
if err != nil {
278+
log.Fatal(err)
279+
}
280+
281+
// 访问结构化数据
282+
fmt.Printf("总页数: %d\n", result.TotalPages)
283+
fmt.Printf("总行数: %d\n", result.TotalLines)
284+
285+
// 遍历每一页
286+
for _, page := range result.Pages {
287+
fmt.Printf("页码: %d, 行数: %d\n", page.PageNumber, page.TotalLines)
288+
for i, line := range page.Lines {
289+
fmt.Printf("%d: %s\n", i, line)
290+
}
291+
}
292+
293+
// 获取完整内容
294+
fmt.Println(result.Content)
295+
```
296+
297+
### 文本清理
298+
299+
DocReader 提供了智能的文本清理功能,可以优化提取的文本内容,特别适合用于大模型处理。
300+
301+
#### 使用预设清理配置
302+
303+
```go
304+
// 使用默认清理(推荐)
305+
doc, err := docreader.ReadDocumentWithClean("document.pdf")
306+
// 效果:移除行首尾空格、压缩多余空格、移除控制字符、最多保留1个连续空行
307+
308+
// 最小清理(保留格式)
309+
doc, err := docreader.ReadDocument("document.txt")
310+
doc.CleanContentMinimal()
311+
// 效果:仅移除行首尾空格、压缩多余空格、移除控制字符,保留所有空行
312+
313+
// 激进清理(最大压缩)
314+
doc, err := docreader.ReadDocument("document.docx")
315+
doc.CleanContentAggressive()
316+
// 效果:移除所有空行,压缩空格,移除控制字符
317+
```
318+
319+
#### 自定义清理配置
320+
321+
```go
322+
// 创建自定义清理器
323+
cleaner := &docreader.TextCleaner{
324+
TrimSpaces: true, // 移除行首行尾空格
325+
RemoveExtraSpaces: true, // 压缩连续空格为一个
326+
RemoveControlChars: true, // 移除控制字符
327+
MaxBlankLines: 2, // 最多保留 2 个连续空行(-1=不限制,0=移除所有)
328+
}
329+
330+
// 在读取时应用清理
331+
doc, err := docreader.ReadDocumentWithCleanConfig("document.pdf", cleaner)
332+
333+
// 或者对已读取的文档应用清理
334+
doc, err := docreader.ReadDocument("document.txt")
335+
doc.CleanContentWith(cleaner)
336+
```
337+
338+
#### TextCleaner 配置说明
339+
340+
```go
341+
type TextCleaner struct {
342+
// TrimSpaces: 是否移除行首行尾空格
343+
TrimSpaces bool
344+
345+
// RemoveExtraSpaces: 是否将连续空格压缩为一个
346+
RemoveExtraSpaces bool
347+
348+
// RemoveControlChars: 是否移除特殊控制字符(保留换行符和制表符)
349+
RemoveControlChars bool
350+
351+
// MaxBlankLines: 最大连续空行数
352+
// -1: 不限制,保留所有空行
353+
// 0: 移除所有空行
354+
// 1: 最多保留 1 个连续空行(压缩多余空行)
355+
// N: 最多保留 N 个连续空行
356+
MaxBlankLines int
357+
}
358+
```
359+
217360
## API 文档
218361

219362
### 核心接口
@@ -222,13 +365,93 @@ fmt.Println(doc.Content)
222365

223366
自动识别文件格式并读取内容,返回包含内容和元数据的 Document 对象。
224367

368+
#### `ReadDocumentWithClean(filePath string) (*Document, error)`
369+
370+
读取文档并自动应用默认文本清理。
371+
372+
#### `ReadDocumentWithCleanConfig(filePath string, cleaner *TextCleaner) (*Document, error)`
373+
374+
读取文档并应用自定义文本清理配置。
375+
376+
#### `ReadDocumentWithConfig(filePath string, config *ReadConfig) (*DocumentResult, error)`
377+
378+
根据配置精确读取文档,返回结构化的结果。
379+
380+
#### `NewReadConfig() *ReadConfig`
381+
382+
创建一个新的读取配置对象,支持链式调用。
383+
225384
#### `DocumentReader` 接口
226385

227386
所有读取器都实现此接口:
228387

229388
- `ReadText(filePath string) (string, error)` - 读取文本内容
230389
- `GetMetadata(filePath string) (map[string]string, error)` - 获取元数据
231390

391+
#### `ConfigurableReader` 接口
392+
393+
支持高级配置的读取器接口(所有读取器都实现):
394+
395+
- `ReadWithConfig(filePath string, config *ReadConfig) (*DocumentResult, error)` - 根据配置读取文档
396+
397+
### 配置结构
398+
399+
#### ReadConfig 配置方法
400+
401+
```go
402+
// 页面选择
403+
config.WithPages(pages ...int) // 设置要读取的离散页码
404+
config.WithPageRange(start, end int) // 添加页码范围
405+
406+
// 全局行选择(应用到所有页)
407+
config.WithLines(lines ...int) // 设置要读取的离散行号
408+
config.WithLineRange(start, end int) // 添加行号范围
409+
410+
// 页面级行配置(覆盖全局配置)
411+
config.AddPageLines(pageIndex int, lines ...int) // 为指定页添加离散行
412+
config.AddPageLineRange(pageIndex, start, end int) // 为指定页添加行范围
413+
config.AddPageConfig(pageIndex int, lineIndexes []int, lineRanges [][2]int) // 为指定页添加完整配置
414+
415+
// XLSX 特有
416+
config.WithSheetNames(names ...string) // 设置要读取的工作表名称
417+
```
418+
419+
#### 核心数据结构
420+
421+
```go
422+
// Selector 统一的选择器,用于选择页码或行号
423+
type Selector struct {
424+
Indexes []int // 离散索引:[0, 2, 5]
425+
Ranges [][2]int // 连续范围:[[0,2], [5,10]]
426+
}
427+
428+
// ReadConfig 读取配置
429+
type ReadConfig struct {
430+
PageSelector Selector // 页面选择器
431+
LineSelector Selector // 全局行选择器
432+
PageConfigs []PageConfig // 页面级配置(优先级高于全局)
433+
SheetNames []string // XLSX 工作表名称
434+
}
435+
436+
// DocumentResult 结构化的文档读取结果
437+
type DocumentResult struct {
438+
FilePath string
439+
Pages []PageContent // 结构化页面内容
440+
TotalPages int
441+
TotalLines int
442+
Metadata map[string]string
443+
Content string // 完整文本内容
444+
}
445+
446+
// PageContent 单页内容
447+
type PageContent struct {
448+
PageNumber int
449+
PageName string // 工作表名称(XLSX)
450+
Lines []string
451+
TotalLines int
452+
}
453+
```
454+
232455
### 专用读取器
233456

234457
#### DocxReader

csv.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,51 @@ func (r *CsvReader) GetRecords(filePath string) ([][]string, error) {
9191

9292
return records, nil
9393
}
94+
95+
// ReadWithConfig 根据配置读取 CSV 文件,返回结构化结果
96+
func (r *CsvReader) ReadWithConfig(filePath string, config *ReadConfig) (*DocumentResult, error) {
97+
file, err := os.Open(filePath)
98+
if err != nil {
99+
return nil, WrapError("CsvReader.ReadWithConfig", filePath, ErrFileOpen)
100+
}
101+
defer file.Close()
102+
103+
reader := csv.NewReader(file)
104+
records, err := reader.ReadAll()
105+
if err != nil {
106+
return nil, WrapError("CsvReader.ReadWithConfig", filePath, ErrFileRead)
107+
}
108+
109+
result := &DocumentResult{
110+
FilePath: filePath,
111+
TotalPages: 1,
112+
Pages: make([]PageContent, 0),
113+
Metadata: make(map[string]string),
114+
}
115+
116+
// 获取元数据
117+
metadata, _ := r.GetMetadata(filePath)
118+
result.Metadata = metadata
119+
120+
// 将每行记录转换为字符串
121+
lines := make([]string, 0, len(records))
122+
for rowIndex, record := range records {
123+
line := fmt.Sprintf("Row %d: %s", rowIndex+1, strings.Join(record, " | "))
124+
lines = append(lines, line)
125+
}
126+
127+
// 根据配置筛选行
128+
filteredLines := filterLinesForSinglePage(lines, config)
129+
130+
pageContent := PageContent{
131+
PageNumber: 0,
132+
Lines: filteredLines,
133+
TotalLines: len(filteredLines),
134+
}
135+
136+
result.Pages = append(result.Pages, pageContent)
137+
result.TotalLines = len(filteredLines)
138+
result.Content = strings.Join(filteredLines, "\n")
139+
140+
return result, nil
141+
}

docx.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,103 @@ func (r *DocxReader) GetMetadata(filePath string) (map[string]string, error) {
148148

149149
return metadata, nil
150150
}
151+
152+
// ReadWithConfig 根据配置读取 DOCX 文件,返回结构化结果
153+
// DOCX 文件以段落为单位,将每个段落视为一行
154+
func (r *DocxReader) ReadWithConfig(filePath string, config *ReadConfig) (*DocumentResult, error) {
155+
zipReader, err := zip.OpenReader(filePath)
156+
if err != nil {
157+
return nil, WrapError("DocxReader.ReadWithConfig", filePath, ErrFileOpen)
158+
}
159+
defer zipReader.Close()
160+
161+
// 查找并读取 document.xml
162+
var documentXML []byte
163+
for _, file := range zipReader.File {
164+
if file.Name == "word/document.xml" {
165+
rc, err := file.Open()
166+
if err != nil {
167+
return nil, WrapError("DocxReader.ReadWithConfig", filePath, ErrFileRead)
168+
}
169+
documentXML, err = io.ReadAll(rc)
170+
rc.Close()
171+
if err != nil {
172+
return nil, WrapError("DocxReader.ReadWithConfig", filePath, ErrFileRead)
173+
}
174+
break
175+
}
176+
}
177+
178+
if documentXML == nil {
179+
return nil, WrapError("DocxReader.ReadWithConfig", filePath, ErrInvalidFormat)
180+
}
181+
182+
// 解析 XML
183+
var doc WordDocument
184+
if err := xml.Unmarshal(documentXML, &doc); err != nil {
185+
return nil, WrapError("DocxReader.ReadWithConfig", filePath, ErrFileParse)
186+
}
187+
188+
result := &DocumentResult{
189+
FilePath: filePath,
190+
TotalPages: 1, // DOCX 作为单页处理
191+
Pages: make([]PageContent, 0),
192+
Metadata: make(map[string]string),
193+
}
194+
195+
// 获取元数据
196+
metadata, _ := r.GetMetadata(filePath)
197+
result.Metadata = metadata
198+
199+
// 提取所有段落和表格行
200+
lines := make([]string, 0)
201+
202+
// 提取段落文本
203+
for _, para := range doc.Body.Paragraphs {
204+
var lineBuilder strings.Builder
205+
for _, run := range para.Runs {
206+
lineBuilder.WriteString(run.Text)
207+
}
208+
line := lineBuilder.String()
209+
if line != "" {
210+
lines = append(lines, line)
211+
}
212+
}
213+
214+
// 提取表格文本
215+
for _, table := range doc.Body.Tables {
216+
for _, row := range table.Rows {
217+
var rowBuilder strings.Builder
218+
for cellIndex, cell := range row.Cells {
219+
if cellIndex > 0 {
220+
rowBuilder.WriteString("\t")
221+
}
222+
for _, para := range cell.Paragraphs {
223+
for _, run := range para.Runs {
224+
rowBuilder.WriteString(run.Text)
225+
rowBuilder.WriteString(" ")
226+
}
227+
}
228+
}
229+
line := strings.TrimSpace(rowBuilder.String())
230+
if line != "" {
231+
lines = append(lines, line)
232+
}
233+
}
234+
}
235+
236+
// 根据配置筛选行
237+
filteredLines := filterLinesForSinglePage(lines, config)
238+
239+
pageContent := PageContent{
240+
PageNumber: 0,
241+
Lines: filteredLines,
242+
TotalLines: len(filteredLines),
243+
}
244+
245+
result.Pages = append(result.Pages, pageContent)
246+
result.TotalLines = len(filteredLines)
247+
result.Content = strings.Join(filteredLines, "\n")
248+
249+
return result, nil
250+
}

0 commit comments

Comments
 (0)