十四、字符、字符串和文本处理(Characters, Strings, and Text Handling)

✅ 第14章:字符、字符串和文本处理(Characters, Strings, and Text Handling)


📌 一、System.Char(字符)

  • .NET 中的字符为 System.Char,本质是 16 位 Unicode 字符(UTF-16 编码单元)
  • 每个字符支持 字符分类、大小写转换、数字检测 等操作:
char c = 'A';
Console.WriteLine(char.IsLetter(c));     // true
Console.WriteLine(char.ToLower(c));      // 'a'

⚠️ 注意:某些字符如 emoji 是由两个 UTF-16 单元组成(称为 surrogate pairs)。


📘 二、System.String(字符串)

  • System.String不可变(immutable) 的引用类型,一旦创建就不能修改
  • 字符串的每次“修改”都会生成新实例(拼接、截取等)

CLR知道String类型中定义的字段如何布局,会直接访问这些字段。但为了获得这种件能和直接访问的好处,String 只能是密封类。

设计原因:

  • 安全(线程安全)
  • 效率(字符串池复用)
  • 可哈希(字符串哈希值稳定)

System.Environment类型定义了只读 NewLine 属性. 应用程序在MicrosoftWindows 上运行时,该属性返回由回车符和换行符 构成的字符串。 NewLine 属性对平台敏感,会根据底层平台来返回恰当 的字符串。 例如,如果将公共语言基础结构(CLI)移植到 UNIX 系统, NewLine 属性将返回由单字符\n构成的宇符串。 以下才是定义上述字符 串的正确方式,它在任何平台上都能正确工作: String s= "Hi"+ Environment.NewLine + "there.";


⚙️ 三、高效构造字符串:StringBuilder

  • 当需要频繁修改字符串时,推荐使用:
var sb = new StringBuilder();
sb.Append("Hello");
sb.Append(" World");
Console.WriteLine(sb.ToString()); // Hello World
操作 推荐类型
读取 + 不变 string
频繁拼接/修改 StringBuilder

StringBuilder 只有以下两种情况才会分配新对象。

  • 动态构造字符串,其长度超过了设置的"容量" 。
  • 调用StringBuilderToString 方法。

🔄 四、ToString 与 Parse:对象与字符串的双向转换

✅ ToString()

  • 任何类型都可覆盖 ToString(),提供格式化输出
DateTime dt = DateTime.Now;
Console.WriteLine(dt.ToString("yyyy-MM-dd"));

✅ Parse()

  • 将字符串转换为对象
int x = int.Parse("123");
DateTime d = DateTime.Parse("2024-01-01");

⚠️ 建议使用 TryParse(),避免格式异常:

if (int.TryParse("abc", out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Invalid number");

🌐 五、编码:字符 <--> 字节转换

.NET 中最常用的编码器有:

编码器 特点
UTF8Encoding 推荐默认,节省空间,兼容性强
UnicodeEncoding UTF-16,每个字符两个字节
ASCIIEncoding 只支持 0–127 的 ASCII 字符,已过时

示例:

string s = "你好";
byte[] utf8 = Encoding.UTF8.GetBytes(s);
string back = Encoding.UTF8.GetString(utf8);

🔐 六、安全字符串(SecureString)

  • SecureString 将字符串内容加密存储在内存中,防止明文字符串被转储泄露

⚠️ 仅在极安全场景使用,如登录凭据处理,且仅限 .NET Framework,.NET Core 中已弃用


📊 Mermaid 图:字符串操作流程图

flowchart TD A[输入字符串] --> B[是否频繁修改?] B -- 是 --> C[StringBuilder 构造] B -- 否 --> D[直接使用 string] D --> E[拼接/格式化] C --> E E --> F[ToString / 输出] A --> G[需要转换为对象?] G --> H[Parse or TryParse]

🎯 七、C# string 类型高阶技巧


1️⃣ 避免隐性堆分配:用 string.Concat() 替代 +

string s1 = "Hello";
string s2 = "World";
string result = string.Concat(s1, s2); // ✅ 性能更优

string r2 = s1 + s2; // ❌ 编译器生成额外中间字符串,可能堆分配

Concat 不创建中间对象链,效率更高(尤其在循环中)


2️⃣ 频繁拼接用 StringBuilder,但小拼接用 string.Create(.NET Core+)

string s = string.Create(10, 'X', (span, state) =>
{
    for (int i = 0; i < span.Length; i++) span[i] = state;
});
Console.WriteLine(s); // "XXXXXXXXXX"

避免堆分配 + 原地构造,用于高频字符串生成或性能敏感路径


3️⃣ 了解字符串池机制(String Interning)

string a = "test";
string b = "te" + "st"; // 编译期拼接,自动驻留
Console.WriteLine(object.ReferenceEquals(a, b)); // true

使用 string.Intern(str) 可手动将运行时字符串驻留到池中,节省内存。

⚠️ 不当使用会导致永久内存泄漏(字符串池常驻内存)


4️⃣ Span 处理子字符串,无堆分配

ReadOnlySpan<char> span = "Hello World".AsSpan();
ReadOnlySpan<char> word = span.Slice(6, 5);
Console.WriteLine(word.ToString()); // "World"

Span<T> 是结构体,不分配堆内存,可用于高性能文本解析(如 JSON/XML Parser)


5️⃣ Use string.Equals(a, b, StringComparison.Ordinal),避免文化陷阱

string a = "straße";
string b = "STRASSE";

// ❌ 默认是 CurrentCultureIgnoreCase,结果为 true
Console.WriteLine(a.Equals(b, StringComparison.CurrentCultureIgnoreCase));

// ✅ 推荐
Console.WriteLine(a.Equals(b, StringComparison.OrdinalIgnoreCase)); // false

✅ 面试点:字符串比较若为 ID、Key、Token 一类值,必须用 Ordinal 比较!

6️⃣ 字符串只读,但可通过 unsafe 修改(黑魔法,慎用)

string s = "hello";
unsafe
{
    fixed (char* p = s)
    {
        p[0] = 'H';
    }
}
Console.WriteLine(s); // "Hello"

⚠️ 会破坏字符串不可变性,影响安全性,仅供学习参考或极端性能压榨。

7️⃣ Regex Cache:避免重复创建正则表达式

static readonly Regex _emailRegex = new Regex(@"^\S+@\S+\.\S+$", RegexOptions.Compiled);

✅ 使用 RegexOptions.Compiled 提升性能 + 缓存静态实例,避免内存泄漏与GC压力


🧠 总结表格

技巧 说明
string.Concat 拼接性能优于 +
string.Create 原地构造,0分配
string.Intern 控制池驻留,减少重复常量
Span<char> 不创建新字符串的子串处理
StringComparison.Ordinal 高性能 + 避免文化问题
unsafe 改写字符 不推荐,仅限特殊场景
Regex 编译 + 缓存 高性能模式匹配方式

🧠 面试题精选


1️⃣ 为什么 string 是 immutable?性能不是更差吗?

✅ 为了线程安全、哈希可用性、共享字符串池;若频繁操作,应用 StringBuilder 替代。


2️⃣ 使用 StringBuilder 能节省多少内存?

✅ 具体数值依赖操作次数,但在大规模拼接场景中可减少数百倍的分配与垃圾回收。


3️⃣ 如何安全地从字符串转换为数值?

✅ 使用 TryParse(),可避免格式异常,提升健壮性。


4️⃣ UTF-8 与 UTF-16 在 .NET 中如何选择?

✅ 推荐默认使用 Encoding.UTF8,节省空间;除非与平台交互需要定长才使用 UTF-16。


5️⃣ 什么是 surrogate pair?为啥重要?

✅ UTF-16 中部分字符(如 emoji、部分亚洲文字)需两个 char 组合,开发时需谨慎处理索引与长度。


✅ 总结表格

模块 要点
char 单个 UTF-16 单元表示字符
string 不可变,适合只读处理
StringBuilder 高效拼接,适合频繁修改
ToString / Parse 对象 ↔ 字符串转换方式
编码 UTF-8 推荐、ASCII 避免
安全字符串 特定安全场景使用 SecureString
posted @ 2025-08-26 10:06  世纪末の魔术师  阅读(45)  评论(0)    收藏  举报