常用字符串函数
概述
PHP 提供了丰富的内置字符串处理函数,涵盖长度计算、截取、查找、替换、大小写转换、填充、分割等多个方面。本章对这些常用函数进行系统分类讲解,并提供实际使用示例。
基础概念
函数分类一览
| 分类 | 函数 | 说明 |
|---|---|---|
| 长度计算 | strlen, mb_strlen | 获取字符串长度 |
| 截取 | substr, mb_substr | 截取子字符串 |
| 查找 | strpos, strrpos, strripos, strstr | 查找子串位置 |
| 替换 | str_replace, substr_replace | 替换子字符串 |
| 大小写 | strtolower, strtoupper, ucfirst, lcfirst, ucwords | 大小写转换 |
| 空白处理 | trim, ltrim, rtrim | 去除空白字符 |
| 填充 | str_pad, str_repeat | 字符串填充和重复 |
| 分割 | chunk_split, wordwrap | 字符串分割 |
| 反转 | strrev | 字符串反转 |
| 随机 | str_shuffle | 随机打乱字符 |
语法与代码
长度与截取
php
<?php
declare(strict_types=1);
// strlen - 获取字符串字节长度
echo strlen('Hello'); // 5
echo strlen('你好'); // 6(UTF-8 中每个中文 3 字节)
// substr - 截取子字符串
echo substr('Hello, World!', 0, 5); // Hello
echo substr('Hello, World!', 7); // World!
echo substr('Hello, World!', -6); // orld!
echo substr('Hello, World!', -6, -1); // orld
// substr 作为数组访问的安全替代
$str = 'Hello';
echo substr($str, 0, 1); // H(等同于 $str[0],但更安全)
echo substr($str, -1); // o(最后一个字符)查找函数
php
<?php
declare(strict_types=1);
// strpos - 查找子串首次出现的位置(区分大小写)
$haystack = 'Hello, World!';
echo strpos($haystack, 'World'); // 7
echo strpos($haystack, 'world'); // false(区分大小写)
// stripos - 不区分大小写的查找
echo stripos($haystack, 'WORLD'); // 7
// strrpos - 查找子串最后出现的位置
echo strrpos('abcabcabc', 'abc'); // 6
echo strripos('abcABCabc', 'ABC'); // 6(不区分大小写)
// strstr - 返回从匹配位置开始的子字符串
echo strstr('Hello, World!', 'World'); // World!
echo strstr('Hello, World!', 'World', true); // Hello, (返回匹配之前的部分)
// 注意:strpos 返回 false 或 0,必须用 === 比较
if (strpos($haystack, 'Hello') === 0) {
echo '以 Hello 开头';
}替换函数
php
<?php
declare(strict_types=1);
// str_replace - 替换所有匹配的子字符串
$text = 'Hello, World! Hello, PHP!';
echo str_replace('Hello', 'Hi', $text); // Hi, World! Hi, PHP!
// str_ireplace - 不区分大小写的替换
echo str_ireplace('hello', 'Hi', $text); // Hi, World! Hi, PHP!
// 数组批量替换
$replace = ['Hello' => 'Hi', 'World' => 'Earth', 'PHP' => 'Code'];
echo str_replace(array_keys($replace), array_values($replace), $text);
// str_replace 统计替换次数(PHP 8.0+)
$count = 0;
str_replace('Hello', 'Hi', $text, $count);
echo $count; // 2
// substr_replace - 基于位置替换
echo substr_replace('Hello, World!', 'PHP', 7, 5); // Hello, PHP!
// 删除子字符串
echo str_replace('World', '', $text); // Hello, ! Hello, !大小写转换
php
<?php
declare(strict_types=1);
// strtolower - 转为小写
echo strtolower('Hello WORLD'); // hello world
// strtoupper - 转为大写
echo strtoupper('hello world'); // HELLO WORLD
// ucfirst - 首字母大写
echo ucfirst('hello world'); // Hello world
// lcfirst - 首字母小写
echo lcfirst('Hello World'); // hello World
// ucwords - 每个单词首字母大写
echo ucwords('hello world php'); // Hello World Php
// mb_convert_case - 多字节安全的大小写转换
echo mb_convert_case('hello WORLD', MB_CASE_TITLE, 'UTF-8'); // Hello World
// 注意:默认大小写转换只对 ASCII 字符有效
echo strtoupper('café'); // Déjà 被处理为 CAFé(部分字符不转换)
echo strtoupper('café'); // 需要 mb_strtoupper
echo mb_strtoupper('café', 'UTF-8'); // Déjà -> CAFé -> 正确: CAFÉ空白处理与填充
php
<?php
declare(strict_types=1);
// trim - 去除两端空白字符(空格、制表符、换行等)
echo trim(' hello '); // hello
echo trim("\t\n hello \n\t"); // hello
// ltrim / rtrim - 去除左/右侧空白
echo ltrim(' hello'); // hello
echo rtrim('hello '); // hello
// trim 也可以去除指定字符
echo trim('***hello***', '*'); // hello
// str_pad - 字符串填充
echo str_pad('42', 8, '0', STR_PAD_LEFT); // 00000042
echo str_pad('42', 8, '-'); // 42------
echo str_pad('42', 8, '-', STR_PAD_BOTH); // ---42---
// str_repeat - 重复字符串
echo str_repeat('ab', 3); // ababab
echo str_repeat('-', 40); // ----------------------------------------分割与换行处理
php
<?php
declare(strict_types=1);
// chunk_split - 按固定长度分割字符串
echo chunk_split('ABCDEFGH', 2, '-'); // AB-CD-EF-GH-
echo chunk_split('1234567890', 4, ' '); // 1234 5678 90
// wordwrap - 按单词边界换行
$text = 'The quick brown fox jumps over the lazy dog.';
echo wordwrap($text, 20, "\n"); // 每 20 个字符换行
echo wordwrap($text, 20, '<br>'); // HTML 换行
// explode / implode - 字符串分割与连接
$fruits = 'apple,banana,cherry';
$arr = explode(',', $fruits); // ['apple', 'banana', 'cherry']
echo implode(' | ', $arr); // apple | banana | cherry
// explode 限制分割数量
$parts = explode(',', 'a,b,c,d,e', 3); // ['a', 'b', 'c,d,e']反转与随机
php
<?php
declare(strict_types=1);
// strrev - 反转字符串(注意:对多字节字符不安全)
echo strrev('Hello'); // olleH
echo strrev('你好'); // 好你(仅适用于 GBK 等双字节编码,UTF-8 会乱码)
// str_shuffle - 随机打乱字符顺序
$str = 'abcdef';
echo str_shuffle($str); // 例如: dbfcea(每次结果不同)
// 应用:生成随机验证码
function generateCode(int $length = 6): string
{
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($chars, (int)ceil($length / strlen($chars)))), 0, $length);
}
echo generateCode(6); // 例如: xK9mQ2详细说明
函数参数类型
PHP 8.0+ 参数类型改进
PHP 8.0 以后,许多字符串函数对 null 参数的处理更加严格。例如 strlen(null) 在 PHP 7.4 返回 0,在 PHP 8.0+ 抛出 TypeError。
str_replace vs preg_replace
| 特性 | str_replace | preg_replace |
|---|---|---|
| 匹配方式 | 固定字符串 | 正则表达式 |
| 性能 | 更快 | 较慢 |
| 灵活性 | 低 | 高 |
| 批量替换 | 支持数组 | 支持数组 |
| 计数 | PHP 8.0+ 通过第 4 参数 | 通过第 5 参数 |
strpos 返回值陷阱
php
<?php
declare(strict_types=1);
$str = 'Hello, World!';
// 错误:strpos 在位置 0 找到时,0 == false 为真
if (strpos($str, 'Hello')) {
// 不会执行!因为 strpos 返回 0
}
// 正确:使用严格比较
if (strpos($str, 'Hello') !== false) {
echo '找到了';
}
// PHP 8.0+ 更好的替代方案
if (str_contains($str, 'Hello')) {
echo '找到了';
}实战示例
URL 规范化
php
<?php
declare(strict_types=1);
function normalizeUrl(string $url): string
{
$url = strtolower($url);
$url = trim($url);
$url = preg_replace('#^https?://#', '', $url);
$url = rtrim($url, '/');
return 'https://' . $url;
}
echo normalizeUrl(' HTTP://Example.COM/path/ ');
// 输出: https://example.com/pathCSV 数据解析
php
<?php
declare(strict_types=1);
function parseSimpleCsv(string $csvLine): array
{
$csvLine = trim($csvLine);
$fields = [];
foreach (explode(',', $csvLine) as $field) {
$field = trim($field);
$fields[] = trim($field, '"\'');
}
return $fields;
}
$data = '"Alice",30,"Developer"';
print_r(parseSimpleCsv($data));
// ['Alice', '30', 'Developer']文件扩展名处理
php
<?php
declare(strict_types=1);
function getExtension(string $filename): string
{
return strtolower(substr(strrchr($filename, '.'), 1));
}
function removeExtension(string $filename): string
{
$pos = strrpos($filename, '.');
return $pos !== false ? substr($filename, 0, $pos) : $filename;
}
echo getExtension('photo.JPG'); // jpg
echo getExtension('archive.tar.gz'); // gz
echo removeExtension('document.pdf'); // document字符串函数完整分类表
截取与查找
| 函数 | 说明 | 多字节安全 |
|---|---|---|
substr($str, $start, $len) | 截取子字符串 | 否 |
mb_substr($str, $start, $len, $enc) | 多字节截取 | 是 |
strstr($haystack, $needle) | 返回 needle 首次出现到末尾 | 否 |
stristr($haystack, $needle) | 不区分大小写版本 | 否 |
strrchr($haystack, $needle) | 返回 needle 最后出现到末尾 | 否 |
strpbrk($haystack, $charList) | 返回包含 charList 中任一字符的位置 | 否 |
substr_count($haystack, $needle) | 统计子串出现次数 | 否 |
substr_replace($str, $replace, $start, $len) | 基于位置替换 | N/A |
chunk_split($str, $len, $end) | 按长度分割并加后缀 | N/A |
编码转换
php
<?php
declare(strict_types=1);
// 字符串编码相关
echo ord('A'); // 65(获取字符 ASCII 码)
echo chr(65); // A(ASCII 码转字符)
echo bin2hex('Hello'); // 48656c6c6f
echo hex2bin('48656c6c6f'); // Hello
echo base64_encode('Hello'); // SGVsbG8=
echo base64_decode('SGVsbG8='); // Hello
echo urlencode('Hello World'); // Hello+World
echo urldecode('Hello+World'); // Hello World
echo rawurlencode('Hello World'); // Hello%20World
echo nl2br("Line1\nLine2"); // Line1<br>Line2
// quoted_printable_encode/decode - 用于邮件编码
$encoded = quoted_printable_encode('你好');
$decoded = quoted_printable_decode($encoded);
// convert_uuencode/convert_uudecode - UU 编码
$encoded = convert_uuencode('Hello');
$decoded = convert_uudecode($encoded);字符串比较
php
<?php
declare(strict_types=1);
// strcmp - 区分大小写的字符串比较
echo strcmp('abc', 'abc'); // 0(相等)
echo strcmp('abc', 'abd'); // -1(小于)
echo strcmp('abd', 'abc'); // 1(大于)
// strcasecmp - 不区分大小写
echo strcasecmp('ABC', 'abc'); // 0
// strncmp - 比较前 n 个字符
echo strncmp('abcde', 'abcxy', 3); // 0(前3个字符相同)
// strnatcmp - 自然顺序比较
echo strnatcmp('img2.png', 'img10.png'); // -1(2 < 10)
echo strcmp('img2.png', 'img10.png'); // 1('2' > '1')
// strcoll - 基于区域设置的字符串比较
$locale = setlocale(LC_COLLATE, 'zh_CN.UTF-8');
echo strcoll('中', '英'); // 比较结果取决于区域设置
// similar_text - 计算两个字符串的相似度
similar_text('Hello World', 'Hello PHP', $percent);
echo $percent; // 相似度百分比常用字符串工具函数
php
<?php
declare(strict_types=1);
// 判断是否为 JSON 字符串
function isJson(string $str): bool
{
json_decode($str);
return json_last_error() === JSON_ERROR_NONE;
}
// 生成随机字符串
function randomString(int $length = 16): string
{
return substr(bin2hex(random_bytes($length)), 0, $length);
}
// 字符串截断(不截断单词)
function truncateWords(string $str, int $maxWords, string $suffix = '...'): string
{
$words = explode(' ', trim($str));
if (count($words) <= $maxWords) {
return $str;
}
return implode(' ', array_slice($words, 0, $maxWords)) . $suffix;
}
echo truncateWords('The quick brown fox jumps over the lazy dog', 4);
// The quick brown fox...
// 安全截断 HTML(不截断标签)
function truncateHtml(string $html, int $maxLength): string
{
preg_match_all('/<[^>]+>|[^<]+/', $html, $tokens);
$result = '';
$openTags = [];
foreach ($tokens[0] as $token) {
if (preg_match('/^<(\w+)/', $token, $tagMatch)) {
$result .= $token;
if (!preg_match('/\/>$/', $token) && !preg_match('/<\/' . $tagMatch[1] . '/', $token)) {
$openTags[] = $tagMatch[1];
}
} elseif (preg_match('/^<\/(\w+)/', $token, $tagMatch)) {
$result .= $token;
array_pop($openTags);
} else {
$remaining = $maxLength - mb_strlen($result, 'UTF-8');
if ($remaining <= 0) break;
$result .= mb_substr($token, 0, $remaining, 'UTF-8');
}
if (mb_strlen($result, 'UTF-8') >= $maxLength) break;
}
// 关闭未闭合的标签
while (!empty($openTags)) {
$result .= '</' . array_pop($openTags) . '>';
}
return $result;
}PHP 8.1+ 新增
PHP 8.1 新增了 array_is_list() 用于检查数组是否为从 0 开始的连续索引数组。这在使用 json_encode 时很有用。
注意事项
编码问题
strlen、substr、strtolower、strtoupper 等函数按字节操作,对非 ASCII 字符(如中文、日文)可能产生错误结果。处理多字节字符应使用 mb_* 系列函数。
strpos 返回值
strpos 找到匹配时返回位置索引(可能为 0),未找到时返回 false。必须使用 === 或 !== 进行比较,不能使用 == 或 !=。
最佳实践
- 优先使用 PHP 8.0+ 新函数:
str_contains、str_starts_with、str_ends_with更安全、更可读 - 多字节字符使用 mbstring:所有涉及中文的操作都应使用
mb_*函数 - 字符串比较使用 ===:避免
strpos返回值的0 == false陷阱 - 批量替换使用数组参数:
str_replace支持数组参数,避免多次调用 - trim 去除用户输入两端空白:所有接收用户输入的场景都应先
trim() - 使用 str_pad 格式化输出:数字补零、表格对齐等场景使用
str_pad()