將長文字依標點符號自動換行,是文字排版常見需求。本文示範使用 String.Split 分割字串,搭配換行符號重組的基本做法,並擴充支援多種標點、保留標點符號、HTML 輸出等進階功能。
using System;
using System.Windows.Forms;
public class TextFormatter
{
public string SplitByPunctuation(string input, char punctuation)
{
if (string.IsNullOrEmpty(input))
return input;
string[] parts = input.Split(punctuation);
string result = "";
for (int i = 0; i < parts.Length; i++)
{
if (i == 0)
result = parts[i];
else
result += "\n" + parts[i];
}
return result;
}
}
public string SplitByPunctuationKeep(string input, params char[] punctuations)
{
if (string.IsNullOrEmpty(input))
return input;
// 使用正則表達式分割,但保留分隔符
string pattern = $"([{Regex.Escape(new string(punctuations))}])";
string[] parts = Regex.Split(input, pattern);
var result = new StringBuilder();
StringBuilder currentLine = new StringBuilder();
foreach (string part in parts)
{
if (punctuations.Contains(part[0]))
{
currentLine.Append(part);
result.AppendLine(currentLine.ToString());
currentLine.Clear();
}
else
{
currentLine.Append(part);
}
}
// 處理最後一行無標點結尾的情況
if (currentLine.Length > 0)
result.Append(currentLine);
return result.ToString();
}
using System.Text.RegularExpressions;
public class AdvancedTextFormatter
{
// 預設中文標點
private static readonly char[] ChinesePunctuations =
{ '。', '!', '?', ';', ':', '、', ',', '.' };
// 預設英文標點
private static readonly char[] EnglishPunctuations =
{ '.', '!', '?', ';', ':', ',' };
public string FormatText(string input,
char[] punctuations = null,
string lineBreak = "\n",
bool trimLines = true,
bool keepPunctuation = true)
{
if (string.IsNullOrEmpty(input))
return input;
punctuations ??= ChinesePunctuations.Concat(EnglishPunctuations).ToArray();
if (keepPunctuation)
{
return SplitKeepPunctuation(input, punctuations, lineBreak, trimLines);
}
else
{
return SplitRemovePunctuation(input, punctuations, lineBreak, trimLines);
}
}
private string SplitKeepPunctuation(string input, char[] punctuations,
string lineBreak, bool trim)
{
string punctuationPattern = Regex.Escape(new string(punctuations));
string pattern = $"([{punctuationPattern}])";
string[] parts = Regex.Split(input, pattern);
var sb = new StringBuilder();
var line = new StringBuilder();
foreach (string part in parts)
{
if (part.Length == 1 && punctuations.Contains(part[0]))
{
line.Append(part);
string lineText = trim ? line.ToString().Trim() : line.ToString();
sb.Append(lineText).Append(lineBreak);
line.Clear();
}
else
{
line.Append(part);
}
}
// 最後一段
if (line.Length > 0)
{
string lineText = trim ? line.ToString().Trim() : line.ToString();
sb.Append(lineText);
}
return sb.ToString();
}
private string SplitRemovePunctuation(string input, char[] punctuations,
string lineBreak, bool trim)
{
string[] parts = input.Split(punctuations, StringSplitOptions.RemoveEmptyEntries);
var sb = new StringBuilder();
for (int i = 0; i < parts.Length; i++)
{
string lineText = trim ? parts[i].Trim() : parts[i];
sb.Append(lineText);
if (i < parts.Length - 1)
sb.Append(lineBreak);
}
return sb.ToString();
}
}
public string FormatForHtml(string input, char[] punctuations = null)
{
string text = FormatText(input, punctuations, "
", true, true);
// HTML 編碼防止 XSS
return System.Web.HttpUtility.HtmlEncode(text).Replace("\n", "
");
}
// WinForms RichTextBox 顯示
private void DisplayFormattedText(string input)
{
var formatter = new AdvancedTextFormatter();
string formatted = formatter.FormatText(input);
richTextBox1.Text = formatted;
}
// WPF TextBlock 顯示 (XAML 中使用 LineBreak)
private void DisplayInWpf(string input)
{
var formatter = new AdvancedTextFormatter();
string formatted = formatter.FormatText(input, lineBreak: "\n");
// 或建立 Inline 集合
textBlock1.Inlines.Clear();
foreach (string line in formatted.Split('\n'))
{
textBlock1.Inlines.Add(new Run(line));
textBlock1.Inlines.Add(new LineBreak());
}
}
public string FormatLargeText(string input, char[] punctuations)
{
// 使用 StringBuilder 避免字串連接產生大量暫存物件
var sb = new StringBuilder(input.Length + 100); // 預估容量
var line = new StringBuilder();
foreach (char c in input)
{
line.Append(c);
if (punctuations.Contains(c))
{
sb.AppendLine(line.ToString().Trim());
line.Clear();
}
}
if (line.Length > 0)
sb.Append(line.ToString().Trim());
return sb.ToString();
}
[TestClass]
public class TextFormatterTests
{
private AdvancedTextFormatter _formatter = new AdvancedTextFormatter();
[TestMethod]
public void TestChinesePunctuation()
{
string input = "你好世界。這是測試!結束。";
string expected = "你好世界。\n這是測試!\n結束。";
Assert.AreEqual(expected, _formatter.FormatText(input));
}
[TestMethod]
public void TestMixedPunctuation()
{
string input = "Hello. World! 你好。";
string expected = "Hello.\n World!\n 你好。";
Assert.AreEqual(expected, _formatter.FormatText(input));
}
[TestMethod]
public void TestNoPunctuation()
{
string input = "沒有標點符號";
Assert.AreEqual(input, _formatter.FormatText(input));
}
[TestMethod]
public void TestEmptyInput()
{
Assert.AreEqual("", _formatter.FormatText(""));
Assert.AreEqual(null, _formatter.FormatText(null));
}
}
| 類別 | 符號 | Unicode | 用途 |
|---|---|---|---|
| 中文句末 | 。 ! ? | U+3002 U+FF01 U+FF1F | 句子結束 |
| 中文句內 | 、 , ; : | U+3001 U+FF0C U+FF1B U+FF1A | 子句分隔 |
| 英文句末 | . ! ? | U+002E U+0021 U+003F | 句子結束 |
| 英文句內 | , ; : | U+002C U+003B U+003A | 子句分隔 |
| 特殊 | … — ~ | U+2026 U+2014 U+FF5E | 省略、連接、波浪 |
依標點分行的核心是 Split 或 Regex.Split 配合 StringBuilder 重組。關鍵在於:是否保留標點、支援哪些標點集合、如何處理最後一段無標點文字。建議封裝為可配置的格式化類別,支援多種輸出格式(純文字、HTML、WPF Inlines),並針對大量文字優化記憶體使用。