文档(1)|PDF转文本删掉多余的换行

梦貘 2026-03-26 11:11

前言

决定开一个新的标签叫文档。昨天读电脑玩物的一篇文章,才意识到AI时代,形成自己的文档和工作流是最重要的。所以这里记录一些可以直接丢给AI,让AI去读取并完成的文档。

Talk is uesless, show AI the code.

(以及:其实下面这个文档也是让AI总结的,算法来源:Copy++DeepWiki总结的

正文

## 核心算法

换行合并的核心算法在 `MainWindow.xaml.cs` 的 `ProcessText` 方法中实现 [1](#0-0) :

```mermaid
flowchart TD
    A["遍历文本字符"] --> B{"检测到 '\r'?"}
    B -->|"否"| C["继续下一个字符"]
    B -->|"是"| D{"前一个字符是句号?"}
    
    D -->|"中文句号'。'"| E{"RemainChinese=true?"}
    D -->|"英文句号'.'"| F{"RemainEnglish=true?"}
    D -->|"其他字符"| G["删除换行符"]
    
    E -->|"是"| C
    E -->|"否"| G
    F -->|"是"| C
    F -->|"否"| G
    
    G --> H{"下一个字符非中文?"}
    H -->|"是"| I["插入空格"]
    H -->|"否"| C
    I --> C
```

## 算法实现细节

### 1. 换行符检测与删除
当检测到 `\r` 字符时,算法会尝试删除2个字符(`\r\n`),如果失败则删除1个字符(`\r`) [2](#0-1) 。

### 2. 智能保留段落换行
算法通过检查换行符前的字符来判断是否应该保留换行 [3](#0-2) :
- 中文句号 `。` + `RemainChinese` 设置 → 保留换行
- 英文句号 `.` + `RemainEnglish` 设置 → 保留换行

### 3. 可读性优化
删除换行后,如果下一个字符不是中文标点、空格或中文字符,算法会自动插入一个空格 [4](#0-3) 。

## 触发机制

这个换行合并功能可以通过以下方式触发 [5](#0-4) :
1. **自动处理**:当 `SwitchMain.IsOn` 为 true 时自动执行
2. **手动处理**:通过 `Manual.xaml.cs` 中的 `MergeLineBtn_Click` 方法手动触发 [6](#0-5) 

## 配置选项

用户可以通过以下设置控制换行合并行为 [7](#0-6) :
- `RemainChinese`:保留中文段落换行
- `RemainEnglish`:保留英文段落换行
- `SwitchMain`:启用/禁用换行合并功能

这个算法巧妙地解决了PDF复制时的格式问题,既清理了多余的换行,又保持了文章的段落结构,大大提升了文本复制的可用性。

## Notes
- 相同的换行合并逻辑在 `Manual.xaml.cs` 中也有实现,用于手动处理场景
- 算法还处理了CAJ阅读器产生的特殊空格字符 `""` [8](#0-7) 
- 该功能是CopyPlusPlus的核心功能之一,专门针对学术论文复制场景优化

Wiki pages you might want to explore:
- [Overview (CopyPlusPlus/CopyPlusPlus)](/wiki/CopyPlusPlus/CopyPlusPlus#1)
- [Text Processing Pipeline (CopyPlusPlus/CopyPlusPlus)](/wiki/CopyPlusPlus/CopyPlusPlus#4)
- [Translation Services (CopyPlusPlus/CopyPlusPlus)](/wiki/CopyPlusPlus/CopyPlusPlus#4.2)

### Citations

**File:** MainWindow.xaml.cs (L53-55)
```csharp
        public bool RemainChinese;
        public bool RemainEnglish;

```

**File:** MainWindow.xaml.cs (L194-195)
```csharp
            // 去掉 CAJ viewer 造成的莫名的空格符号
            text = text.Replace("", "");
```

**File:** MainWindow.xaml.cs (L200-203)
```csharp
            if (SwitchMain.IsOn || SwitchSpace.IsOn)
                if (text.Length > 1)
                    for (var counter = 0; counter < text.Length; ++counter)
                    {
```

**File:** MainWindow.xaml.cs (L205-231)
```csharp
                        if (SwitchMain.IsOn && counter >= 0 && text[counter] == '\r')
                        {
                            if (counter > 0)
                            {
                                // 如果检测到句号结尾,则不去掉换行
                                if (text[counter - 1] == '。' && RemainChinese) continue;
                                if (text[counter - 1] == '.' && RemainEnglish) continue;
                            }

                            // 去除换行
                            try
                            {
                                text = text.Remove(counter, 2);
                            }
                            catch
                            {
                                text = text.Remove(counter, 1);
                            }

                            --counter;

                            // 判断 非负数越界 或 句末
                            if (counter >= 0 && counter != text.Length - 1)
                                // 判断 非中文 结尾, 则加一个空格
                                if (!Regex.IsMatch(text[counter].ToString(), "[\n ,。?!《》\u4e00-\u9fa5]"))
                                    text = text.Insert(counter + 1, " ");
                        }
```

**File:** Manual.xaml.cs (L30-73)
```csharp
        private void MergeLineBtn_Click(object sender, RoutedEventArgs e)
        {
            if (_lineStatus) return;
            _lineStatus = true;

            if (!Clipboard.ContainsText()) return;

            var text = Clipboard.GetText();

            if (text.Length > 1)
                for (var counter = 0; counter < text.Length; ++counter)
                    // 合并换行
                    if (counter >= 0 && text[counter] == '\r')
                    {
                        if (counter > 0)
                        {
                            // 如果检测到句号结尾,则不去掉换行
                            if (text[counter - 1] == '。' && _mainWindow.RemainChinese) continue;
                            if (text[counter - 1] == '.' && _mainWindow.RemainEnglish) continue;
                        }

                        // 去除换行
                        try
                        {
                            text = text.Remove(counter, 2);
                        }
                        catch
                        {
                            text = text.Remove(counter, 1);
                        }

                        --counter;

                        // 判断 非负数越界 或 句末
                        if (counter >= 0 && counter != text.Length - 1)
                            // 判断 非中文 结尾, 则加一个空格
                            if (!Regex.IsMatch(text[counter].ToString(), "[\n ,。?!《》\u4e00-\u9fa5]"))
                                text = text.Insert(counter + 1, " ");
                    }

            Clipboard.SetDataObject(text);

            _lineStatus = false;
        }
```
本文阅读量: