ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

字符串转义与内建函数:跨语言避坑实战指南

字符串转义与内建函数:跨语言避坑实战指南 你有没有遇到过这种情况打印一个日志本来想输出C:\temp\new\file.txt结果终端里却变成了C: emp ew\file.txt或者从接口拿到一段字符串想用.做分隔符split(.)半天切不动最后才发现正则表达式里点号必须写成\\.这类问题十有八九都落在同一个主题上字符串的转义字符与内建函数。字符串本身就是编程里最常用的数据类型而转义字符决定了一个字符串“真正存的是什么东西”内建函数则决定了你能对字符串做哪些操作。两者看似基础实则是排查诡异 bug、写出健壮代码的分水岭。这篇内容写给三类人刚学完语法、正在刷题的在校学生工作中频繁处理日志、接口数据和文本解析的初级程序员还有那些准备面试、想把自己手里的 C/C/Java/Python/C# 字符串处理能力系统整理一遍的朋友。我会用真实场景讲透“为什么需要转义”、常见内建函数的底层逻辑再附上多语言对照和线上排查记录全程无废话可以直接照着用。1. 转义字符为什么说它是字符串世界的地基1.1 转义字符到底在“转”什么先想一个最朴素的问题字符串是用引号包起来的那如果字符串本身内容里要有引号怎么办比如我想在 C 语言里表示一句话他说“你好”。直接写他说你好编译器根本不知道哪个引号是字符串的边界语法直接报错。这个时候就需要“转义”用反斜杠\加特定字符告诉编译器“后面这个符号我不是当语法用的我是当普通内容用的”。再比如换行。你在键盘上敲回车文件里确实存了换行字符但你在代码里写字符串时不可能把一个真正的换行塞进两个引号之间——编辑器会真的给你换行语法就断了。所以才有\n这种东西来表示换行。所谓“转义”本质上是建立了一套字符的“暗号系统”编译器或解释器在解析字符串字面量时遇到反斜杠就知道后面是一个特殊代码它会把这串代码翻译成实际存储的字符。你在源码里看到的字符和运行时内存里的字节往往不是一回事。这个认知很重要因为很多字符串 bug 就是“我看到的是 A程序里存的是 B”。生活类比一下转义字符就像对讲机里的暗号。你对着对讲机说“洞拐”队友听到的其实是数字“07”。源码里的\n就是“洞拐”程序真正存储的、输出的是那个看不见的换行符。1.2 高频转义字符对照与它们的经典舞台不同语言对转义字符的支持大体一致但细节有差异。先把最常用的一批列出来转义写法实际含义典型场景\n换行符 LF日志换行、多行文本拼接\r\n回车换行 CRLFWindows 文本文件、HTTP 协议头\t制表符 Tab对齐表格输出、TSV 数据\\一个反斜杠\Windows 路径、正则表达式\单引号单引号字符串中的引号\双引号双引号字符串中的引号\0空字符 NULC 字符串结束符\uXXXXUnicode 字符如\u4E2D表示“中”Java、Python、C# 中输出特殊字符\xHH十六进制编码的字节C/C、Python 中精确控制字节这里我想重点讲两个“暗坑”。第一个坑是\0。在 C 语言里\0表示字符串结束strlen(abc\0def)的结果是 3不是 7。很多初学者在拼接二进制数据或者处理协议时字符串里明明有内容一用strlen或printf就“截断”了都是\0在作怪。热搜词里有“cstring字符串结束符”说的就是这个。第二个坑是 Windows 路径。C:\temp\new\file.txt在绝大多数语言的字符串字面量里会被解析成C: Tab new 换行 file.txt。为什么因为\t是 Tab\n是换行。你要是直接这么写轻则路径找不到重则写出谁也看不懂的日志。1.3 各语言的“反转义”机制和模板字符串正因为转义字符容易把人绕晕很多语言提供了“关闭转义”的机制Python的原始字符串rC:\temp\new前缀r后反斜杠不再转义路径就安全了。C#的逐字字符串C:\temp\new效果类似而且逐字字符串里写双引号要写成两个双引号。JavaScript的模板字符串用反引号包裹内容好处是支持多行直接书写${变量}做插值但注意反引号本身和${仍需转义写成\和\${。Java目前没有直接的 raw string文本块也只是减少常见转义所以路径里写反斜杠还是得老实写C:\\temp\\new。顺带提一下 MATLAB它的字符串用单引号如果字符串里要表示一个单引号得连续写两个单引号Its OK。这跟 SQL 里字符串中单引号转义的思路一样算是一类特殊规则。还有一个容易混淆的场景正则表达式里的双重转义。正则引擎自己有一套转义规则比如\d表示数字、\.表示普通点号。如果你在 C/Java 这类语言里写正则字符串编译器先吃掉一层反斜杠正则引擎再吃一层。所以“匹配一个点号”的正则写法是\\.在代码里就变成了\\.。很多新手卡在split(.)上问题就出在这。2. 内建函数字符串处理的高频操作拆解字符串内建函数内置方法非常多但真正常用的核心操作就那十几类长度、判空、比较、截取、替换、分割、拼接、查找、包含、大小写转换、去空白、类型转换。我按使用频率逐一拆解并标注跨语言差异这些差异正是日常切换语言时最容易踩的坑。2.1 长度、判空、比较三件最基础也最容易错的事获取字符串长度C 用.size()或.length()Java 用.length()Python 用len()JavaScript 用.length属性C# 用.Length属性。注意区分Java 的length是方法要括号数组的length才是属性JavaScript 里length是属性不用括号Python 中统一的len()是内置函数。C 语言比较特殊char[]没有内建长度要用strlen()遍历计数直到遇到\0。这也是为什么 C 程序员要格外小心\0因为字符串的“长度”不是一个存好的字段而是实时数出来的。判空也有讲究。首先得区分“引用为 null”和“内容为空字符串”是两回事。Java 里String s null;和String s ;完全不同前者调用任何方法都会抛 NullPointerException。所以很多项目里会写一个工具方法public static boolean isBlank(String str) { return str null || str.trim().isEmpty(); }注意这个trim()只处理半角空格。全角空格 U3000 不会去掉这又是一个让人抓狂的细节后面讲去空白时再展开。字符串比较是我见过初学者问得最多的问题。C 语言里不能直接比较两个字符串内容因为数组名是地址比较的是地址要比较内容得用strcmp()。Java 里比较的也是引用值相同但地址不同的字符串对象返回 false必须用equals()。而 C 的std::string和 C# 的string重载了可以直接按内容比较方便很多。Python 直接用比较内容这是解释型语言的便利。为什么会有这种差异本质是“字符串是不是一等公民”的问题C 语言里字符串是字符数组Java/Python 里字符串是对象C/C# 里字符串是重载了运算符的类。理解这一点你就不会再困惑为什么各语言行为不一样了。2.2 截取、替换、分割与拼接工程里用得最多的四个操作截取Python 的切片s[1:3]是左闭右开取下标 1 和 2不含 3。JavaScript 的substring(1, 3)同样左闭右开但slice(1, 3)可以传负数substring不行。Java 的substring(1, 3)也是左闭右开C# 的Substring(1, 2)是“起始位置 长度”。C 没有标准子串函数只能自己用指针或strncpy处理。这些差异里最容易翻车的是边界值。Pythons[0:len(s)]能取全部但s[len(s)]直接越界报错Javasubstring(0, s.length())合法substring(s.length(), s.length())返回空串也不是报错但substring(s.length()1)就会抛异常。写通用代码时一定要先确认语言的下标规则。替换Java 里replace是普通字符串替换replaceAll是正则替换。a.b.replace(., -)会把点号换成横线而a.b.replaceAll(., -)会把每个字符都换成横线因为正则里.表示任意字符。这是用 Java 做文本处理时极常见的坑。JavaScript 的replace默认只替换第一处要全局替换得写正则加g标志a-b-c.replace(/-/g, )。Python 的str.replace(old, new)默认全局替换行为又不一样。分割这是热搜词里出现频率最高的操作之一。Pythona,b,c.split(,)得到列表JavaScripta,b,c.split(,)得到数组C#a,b,c.Split(,)得到数组而且 C# 的Split默认会去掉空条目这跟其他语言行为不同需要注意。Java 的split接收正则所以按点号分割必须写成a.b.c.split(\\.)按竖线分割写成a|b.split(\\|)。C 语言有strtok()函数但它有几个反直觉的特点第一它会在原字符串上直接修改把分隔符替换成\0所以传入的字符串必须是可修改的第二它是用静态内部指针记住位置的不可重入多线程环境下使用会有问题第三连续出现的分隔符会被当成一个处理“a,,b”用逗号分割中间不会得到空字符串。C 里更推荐std::istringstream和std::getline做分割或者手写一个按字符分割的循环可控性更好。拼接简单场景下C 用Java 用或StringBuilderPython 用或join()JavaScript 用或模板字符串。注意循环拼接时的性能差异Java 在循环里用会不断创建新对象数据量大时建议用StringBuilderPython 在循环里用也有类似效率问题更推荐先收集到列表再.join(list)。2.3 查找、包含、大小写与去空白查找包含Pythonin运算符Javacontains()、indexOf()JavaScriptincludes()、indexOf()C#Contains()、IndexOf()Cstd::string::find()。注意几个返回值细节Java/C#/C/JavaScript 的indexOf/find找不到时返回值不同Java 返回-1C 返回std::string::npos一个很大的正数写判断条件时容易出 bug。JavaScript 的includes返回布尔值但indexOf找不到返回-1。如果只用if (!abc.indexOf(b))因为indexOf(b)返回 1!1为 false逻辑就反了正确写法是if (abc.indexOf(b) ! -1)。大小写转换Pythons.upper()/s.lower()JavatoUpperCase()/toLowerCase()C#ToUpper()/ToLower()C 语言要自己遍历字符用toupper()/tolower()。这里有个冷门但真实的坑在 Java 的土耳其语 locale 下i.toUpperCase()得到的是İ带点的大写 I而不是I。所以国际化的系统里固定写toUpperCase(Locale.ENGLISH)更稳妥或者用toUpperCase(Locale.ROOT)。去空白Pythonstrip()默认去首尾空白Javatrim()只去 U0020 到 U00A0 之间的字符全角空格“ ”U3000去不掉Java 11 之后推荐strip()用Character.isWhitespace判断能处理全角空格C#Trim()行为略不同JavaScripttrim()能处理各种空白。另外一个常见的坑用户从 Excel、网页编辑器里复制过来的文本经常带着全角空格trim()不掉这时候你对比两个明明“看起来一样”的字符串永远不相等。安全做法是统一用 Unicode 转义匹配replace(\\u3000, )。2.4 类型转换字符串与数字、枚举、ASCII 的互转字符串转数字我单独拎出来讲因为报错方式和返回值差异巨大语言常用写法非法输入时的行为Catoi()返回 0不报错无法区分“本来就该是0”和“非法”Cstrtol()返回 0 并设置错误码可区分Cstoi()抛std::invalid_argument/out_of_rangeJavaInteger.parseInt()抛NumberFormatExceptionC#int.Parse()抛FormatExceptionC#int.TryParse()返回 false不抛异常输出参数为 0Pythonint()抛ValueErrorSQL ServerCAST/CONVERT直接报错TRY_CAST 返回 NULLSQL ServerTRY_CAST转换失败返回 NULL不报错C 语言的atoi(12abc)能返回 12它是“尽量读读到非法就停”Java 的parseInt(12abc)直接抛异常。这两种行为差异很关键——解析用户输入时你可能想要前者但它容易掩盖数据格式问题想严格校验时你又会需要后者。工程实践里我倾向的做法是涉及业务数据解析一律用“带错误返回”的版本如strtol、TryParse或int外面套异常处理宁可报错也不要静默吞掉脏数据。SQL Server 里还有个 ISNUMERIC 的坑ISNUMERIC(1e3)返回 1因为它把e当成科学计数法但CAST(1e3 AS INT)会失败。所以 SQL Server 2022 之前最稳妥的写法是SELECT TRY_CAST(123abc AS INT); -- 返回 NULL数字转字符串相对简单C 用sprintf/snprintfC 用std::to_stringJava 用String.valueOfPython 用str()C# 用ToString()。枚举类型转字符串也比较常见。C# 里DayOfWeek.Monday.ToString()得到Monday还支持Enum.GetName。Java 里enum有两个相关方法name()返回声明时的名称toString()默认也是名称但可以被重写。如果你在代码里重写了toString()返回中文而其他地方用name()得到的结果会不一样别混用。C 的标准枚举没有内建转字符串能力要么手写switch映射要么用 C23 的std::to_underlying拿到底层值再配合表驱动。C# 字符串转 ASCII 码典型写法是byte[] asciiBytes Encoding.ASCII.GetBytes(ABC); // asciiBytes { 65, 66, 67 }C 和 C 里因为char本身就是整数类型直接(int)A就能拿到 65。Python 用ord(A)拿码点反方向用chr(65)。3. 实战案例字符串逆序、排序与“拼数”问题光讲内建函数有点散我挑三个典型的实战场景把前面提到的知识串起来也顺便回应一下热搜词里出现的那几个题目关键词。3.1 字符串逆序的四种写法与性能差异字符串逆序是最经典的入门题但不同语言的写法和性能差异挺有意思。C 语言双指针原地交换void reverse(char *s) { int left 0, right strlen(s) - 1; while (left right) { char tmp s[left]; s[left] s[right]; s[right] tmp; left; right--; } }注意这里必须先strlen拿到长度因为 C 字符串末尾有\0不能把结束符也交换了。时间复杂度 O(n)空间 O(1)是最标准的写法。Python切片一步到位s hello reversed_s s[::-1]因为 Python 字符串不可变s[::-1]创建新对象简单但占用额外空间。如果面试官问“如何不用切片实现”可以用列表s list(hello) left, right 0, len(s) - 1 while left right: s[left], s[right] s[right], s[left] left 1 right - 1 print(.join(s))C标准库直接接管std::string s hello; std::reverse(s.begin(), s.end());C 的std::reverse是模板函数对std::string的迭代器区间直接反转同样是 O(n)。C#LINQ 的代价string s hello; string reversed new string(s.Reverse().ToArray());Reverse()是 LINQ 延迟迭代ToArray()物化再传给构造函数中间会分配额外内存。性能不是最优的但可读性极高。性能敏感场景推荐char[] arr s.ToCharArray(); Array.Reverse(arr); string reversed new string(arr);从这几个写法能看出一个规律高级语言不是让你不会写逆序而是让你选择“是否牺牲可读性换性能”。日常业务代码我选最简写法但笔试面试时C/C 方向还是老老实实写双指针因为面试官想看你对内存和边界的理解。3.2 字符串排序与“拼数(number)”问题热搜词里有道“拼数”题描述不完整但按这类题最常见的逻辑它大概是给若干个数字字符串要求把它们拼接成一个最大的数或最小的数。比如[3, 30, 34, 5, 9]正确最大结果是9534330。如果直接按字典序排序结果会错。因为3和30比较字典序30 3但拼接时3303 在前却小于30330 在前吗不是应该 30 在前我们来算一下303和330显然330 303所以为了得到更大的结果应该让 3 排在 30 前面。这就是为什么不能直接字典序排序而要自定义比较规则。正确的比较规则是比较 ab 和 ba 的字典序哪个拼接结果大哪个放前面。C 写法bool cmp(const string a, const string b) { return a b b a; } vectorstring nums {3, 30, 34, 5, 9}; sort(nums.begin(), nums.end(), cmp); string result; for (auto s : nums) result s;Python 写法更简洁利用functools.cmp_to_keyfrom functools import cmp_to_key def compare(a, b): if a b b a: return -1 elif a b b a: return 1 else: return 0 nums [3, 30, 34, 5, 9] nums.sort(keycmp_to_key(compare)) print(.join(nums)) # 9534330这里有个细节为什么比较 ab 而不是先转 int 再比较大小因为字符串可能很长转成 int 会溢出而且漏掉前导零的处理比如09和9直接字符串拼接比较既安全又符合原始数据类型。如果全为 0拼接结果是0000还是0这要看题目要求通常是去前导零输出0需要单独处理。这个题的考点本质是排序的比较器不一定是“值比较”也可以是“规则比较”。字符串处理里这种自定义比较器非常常见比如按长度排序、按最后一位排序、按包含关系排序都是在compare函数里做文章。3.3 字符串分割、统计与排序的竞赛组合场景热搜词里有“b4578 [gesp202609 三级] 分割字符串”、“小杨有 n 个仅包含小写字母的字符串”这类关键词。这类 GESP 三级的题考的就是最实用的字符串处理组合拳读入 → 分割 → 统计 → 排序/去重。我按通用套路梳理一下解法思路。假设题目要求是给定一个长字符串用某个分隔符分割出若干单词统计每个单词出现次数按出现次数降序输出次数相同按字典序升序。C 实现思路#include bits/stdc.h using namespace std; int main() { string s; getline(cin, s); mapstring, int cnt; string word; // 假设分隔符是空格 stringstream ss(s); while (ss word) { cnt[word]; } vectorpairstring, int v(cnt.begin(), cnt.end()); sort(v.begin(), v.end(), [](auto a, auto b) { if (a.second ! b.second) return a.second b.second; return a.first b.first; }); for (auto [w, c] : v) { cout w c endl; } return 0; }注意map本身就按 key 排序所以先放map完成统计和字典序排序再转vector按次数二次排序。C 里这种“mapvectorpairsort自定义比较器”的组合几乎能覆盖所有字符串统计类题目。Python 版本用collections.Counter更顺手from collections import Counter s input().strip() words s.split() counter Counter(words) for word, count in sorted(counter.items(), keylambda x: (-x[1], x[0])): print(word, count)Python 排序技巧keylambda x: (-x[1], x[0])用负数表示降序x[0]表示字典序升序一行搞定复合排序。4. 常见坑点与问题排查实录这一节我把实际开发中踩过、也在热搜词里反复出现的典型问题整理成一份“避坑实录”每个问题都给出排查思路。4.1 字符串结束符、宽字符与文件编码的三角关系问题一为什么我用 C/C 拼接字符串内容总是不全多半是\0的位置问题。字符串字面量abc\0def里strlen只算到 3printf(%s)也只输出abc。但如果你用memcpy或std::string(abc\0def, 7)指定长度就能把后面的def也带进去。这也是 C/C 处理二进制协议时的经典坑只要字符串里可能含有\0就不能用字符串函数要用长度 指针的方式操作。问题二C 里用了L...宽字符串为什么工程里乱码、编译报错热搜词里“codeblock 字符串 宽字符 L 表示 出错”这通常是两类问题。第一L...是wchar_t类型编译环境不同wchar_t的宽度也不同Windows 上 16 位Linux 上 32 位不能想当然地把宽字符串直接和窄字符串拼接或用printf输出。第二源码文件的编码和编译器对宽字符的解读如果不一致L中文里的中文可能会被按错误的编码方案转成宽字符导致乱码。排查思路统一源码编码为 UTF-8Windows 下建议使用/utf-8编译选项或者干脆用u8...UTF-8 字面量、u...UTF-16 字面量代替L...C20 之后用std::u8string更规范。问题三IDA 里看中文字符串全是乱码IDA 默认的默认字符串识别可能没启用 UTF-8/UTF-16需要在 Options → String literals 调整编码格式。这是逆向分析工具层面的问题不是程序问题。真正常见的是程序存的是 UTF-8工具按 GBK 解读显示就花了。4.2 序列化与转义字符的爱恨纠葛热搜词里有一条“fastjson 序列化不包括转义字符”我猜遇到的情况是某个字符串字段里存了\n这两个字符反斜杠 字母 n序列化成 JSON 时JSON.toJSONString输出的是\\n反序列化回来却变成了真正的换行符导致“和原来对不上”。这里要理清层次原始字符串内容如果确实是5 个字符a \ n b反斜杠字母n那 JSON 序列化时为了在 JSON 里正确表示这个反斜杠会把它转义为\\n输出是a\\nb。这是正确的因为 JSON 规范要求反斜杠本身必须被转义。如果原始字符串内容是一个换行符那序列化输出就是a\nb。反序列化时JSON 解析器会把a\\nb还原成a\nb5 个字符把a\nb还原成a 换行 b3 个字符。所以如果你的“不包括转义字符”诉求是“JSON 字符串里不要出现\\n”那就得确认你到底想要的是哪个内容如果你是想要字面意义的反斜杠n那序列化输出里出现\\n是必然的不能去掉如果你不想要的是真正的换行符被序列化成\n那是正常的 JSON 行为。工程里还有一种常见诉求拿到的字符串已经带了一层转义比如从数据库读出了\\n字面量想“去掉转义”得到真正换行。那你要做的是解转义比如 Java 里String raw Hello\\nWorld; // 此时 raw 的内容是 Hello 反斜杠 n World String unescaped raw.replace(\\n, \n); // 此时 unescaped 的内容是 Hello 换行 World但要小心连续做两次replace可能把原本就有的\\也处理错最好用正则匹配反斜杠后的转义序列逐个解。类似的还有fastjson的JSON.parseObject(str, Feature.AllowUnQuotedFieldNames)等特性开关有些特性会影响反序列化对转义的处理排查时要注意。Python 的json.dumps也有一个类似的“转义”特性默认ensure_asciiTrue会把中文输出成\u4E2D\u6587这样的 Unicode 转义形式看着像乱码但它只是 JSON 的一种合法编码表示解析回来内容不变。如果想让数据文件对人类可读可以设置ensure_asciiFalse。4.3 判断字母数字、驼峰命名与 ODBC 连接字符串Java 判断字符串中“是否含有非字母数字字符”很多人用正则一行解决boolean hasSpecialChar str.matches(.*[^a-zA-Z0-9].*);这个正则是“.* 任意字符 一个非字母数字字符 .*”所以只要有一个特殊字符就匹配 true。如果要判断“是否全是字母数字”反过来写boolean allAlphanumeric str.matches([a-zA-Z0-9]);注意别漏掉漏了就只能匹配单个字符了。如果要兼容 Unicode 字母数字Java 可以用循环遍历 Character.isLetterOrDigit(ch)正则方案对不同语言支持度不一。判断字符串是否为驼峰命名核心是找到大写字母的位置。正则[A-Z]可以匹配 ASCII 大写字母但复杂一点的驼峰比如getHTTPResponse有三个连续大写字母按单词拆分时规则就要设计通常把连续大写字母归为一组后面接小写字母的大写字母单独拆。Python 里有一个技巧import re name getHTTPResponse parts re.findall(r[A-Z][a-z]*|[A-Z](?[A-Z]|$), name) # parts [get, HTTP, Response]这种“大写字母识别”在解析 XML/JSON 字段名、或者做代码生成器时特别常见比如把XMLParser转成xml_parser。ODBC 连接字符串也是一段特殊格式的字符串常见格式Driver{ODBC Driver 17 for SQL Server};ServermyServer;DatabasemyDB;Uiduser;Pwdpass;它的坑在于值里如果包含分号;、花括号{}或反斜杠需要用花括号把值包起来花括号里的反斜杠和括号也有各自的转义规则{要写成{不实际是}要写成}}。比如密码是p;ass直接写会破坏连接串结构正确做法是Pwd{p;ass}。很多系统上线后连不上数据库排查半天才发现是密码里有个分号。4.4 字符串排序的稳定性与多字节字符C 语言里对字符串数组排序经典写法是用qsortstrcmpint cmp(const void* a, const void* b) { return strcmp(*(const char**)a, *(const char**)b); }注意strcmp的返回值是负数/0/正数不是 -1/0/1直接返回给qsort没问题但如果你自己写比较函数返回固定 -1/0/1在部分排序算法里可能性能稍差但一般不致命。中文排序是一个隐藏大坑。strcmp按字节比较对于 UTF-8 编码的中文比较的是 UTF-8 字节序列结果和字典序拼音/笔画序完全不是一回事。如果想按拼音排序C 里要用std::locale配合std::collatePython 里sorted(words, keylambda s: s)默认也按码点排序同样不对应拼音。C# 的默认比较器StringComparer.Chinese可以按拼音排序但要显式指定文化。这类需求在通讯录、报表排序里很常见一定要明确你想要的到底是“码点序”“字典序”还是“拼音序”。5. 多语言字符串 API 速查对照最后给你一张常用对照表我写代码前经常扫一眼这张表尤其是跨语言切换的时候能少踩好多坑操作CCJavaPythonJavaScriptC#长度strlen(s)s.size()s.length()len(s)s.lengths.Length拼接strcat(dst, src)s1 s2s1 s2s1 s2s1 s2s1 s2比较内容strcmp(a, b)a ba.equals(b)a ba ba b截取手写指针s.substr(pos, len)s.substring(beg, end)s[start:end]s.slice(beg, end)s.Substring(pos, len)查找strchr/strstrs.find(t)s.indexOf(t)t in ss.indexOf(t)s.IndexOf(t)替换手写replace/正则replaceAll/正则s.replace(old,new)s.replace(reg,new)s.Replace(old,new)分割strtokstringstreamsplit(正则)s.split(sep)s.split(sep)s.Split(char)转数字strtolstoiparseIntint()parseInt/Numberint.TryParse大小写toupper/tolower手写/库toUpperCase()s.upper()s.toUpperCase()s.ToUpper()去空白手写手写/boosts.strip()(11)s.strip()s.trim()s.Trim()转字符串sprintfto_stringString.valueOfstr()String(x)x.ToString()注意表格里的“截取”列参数含义不同C 和 C# 是起始位置 长度Java 和 JavaScript 的substring/substr是起始 结束位置不含结束Python 是切片。这是最容易搞混的一列。还有一个容易被忽略的点C 的std::string::substr第二个参数是长度但 Java 的substring第二个参数是 end 索引。从 C 切到 Java很容易写出s.substring(0, 4)结果比预期少取一位。写在最后我这些年和字符串“干架”的心得分享一个我真实的排查经历。早年间做一个日志解析模块线上偶尔出现一行日志被截断的情况查了整整一下午最后发现是某条用户昵称里带了\0字符写入文件后被 C 语言的字符串处理函数直接截断了后面的内容全丢。从那次以后我养成两个习惯第一任何来自外部的字符串在进入业务逻辑之前先做清洗——换行符统一、\0剔除、全角空格转半角顺便校验长度上限。字符串处理不只是 API 调用更是数据治理的第一道防线。第二能序列化就别手拼字符串。手拼 JSON、手拼 SQL 拼接、手拼协议报文都会引入转义和注入风险。用标准库的序列化工具转义规则由库来保证你只需要关注数据本身。最后再送你一个排错的小技巧遇到字符串输出乱了第一件事不是看代码而是把字符串逐字符打成十六进制。换行、Tab、\0、全角空格这些“隐身字符”一上十六进制全现形。Python 里.join(f{ord(c):04x} for c in s)C 里逐字节printf(%02x , (unsigned char)c)几十秒就能定位问题比反复读代码高效得多。字符串处理没有特别高深的理论但它就是编程基本功的试金石。把转义字符吃透、把内建函数的语言差异记牢、把边界情况想全你处理数据时就少一丝心慌多一分笃定。希望这篇整理能帮你少走一些我当时走过的弯路。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进