PHP 字符串包含判断:`str_contains`、`strpos` 与正则表达式 `preg_match` 的深度解析125
在 PHP 开发中,判断一个字符串是否包含另一个特定的子字符串,是日常工作中极其常见的需求。无论是数据验证、搜索功能、日志分析还是内容过滤,这项基本操作都无处不在。然而,面对这一看似简单的任务,PHP 提供了多种实现方式,包括简洁的字符串函数和功能强大的正则表达式。如何选择最适合的方法,不仅关乎代码的简洁性,更影响着程序的性能和可维护性。
本文将作为一份详尽的指南,深入探讨 PHP 中用于字符串包含判断的各种技术。我们将从 PHP 8.0 引入的 `str_contains()` 函数开始,逐步讲解传统的 `strpos()` 和 `strstr()` 函数,并最终聚焦于正则表达式的强大应用——`preg_match()`。通过详细的示例代码、性能分析和最佳实践,您将能够透彻理解每种方法的优缺点,并在实际项目中做出明智的选择。
一、最直接的答案:PHP 8+ `str_contains()` 函数
随着 PHP 8.0 的发布,字符串操作迎来了一个备受期待的函数:`str_contains()`。它的出现,极大地简化了判断字符串是否包含子字符串的逻辑,使其变得更加直观和易读。
1.1 语法与用法
bool str_contains ( string $haystack , string $needle )
参数说明:
`$haystack`:要搜索的主字符串(大字符串)。
`$needle`:要查找的子字符串(小字符串)。
返回值:如果 `$haystack` 中包含 `$needle`,则返回 `true`;否则返回 `false`。
1.2 示例代码
<?php
$text = "Hello, world! This is a PHP string example.";
// 检查是否包含 "PHP"
if (str_contains($text, "PHP")) {
echo "<p>字符串包含 'PHP'.</p>"; // 输出:字符串包含 'PHP'.
} else {
echo "<p>字符串不包含 'PHP'.</p>";
}
// 检查是否包含 "example"
if (str_contains($text, "example")) {
echo "<p>字符串包含 'example'.</p>"; // 输出:字符串包含 'example'.
} else {
echo "<p>字符串不包含 'example'.</p>";
}
// 检查是否包含 "Python" (不存在)
if (str_contains($text, "Python")) {
echo "<p>字符串包含 'Python'.</p>";
} else {
echo "<p>字符串不包含 'Python'.</p>"; // 输出:字符串不包含 'Python'.
}
// 注意:str_contains 是大小写敏感的
if (str_contains($text, "php")) {
echo "<p>字符串包含 'php' (小写).</p>";
} else {
echo "<p>字符串不包含 'php' (小写).</p>"; // 输出:字符串不包含 'php' (小写).
}
?>
1.3 优点与局限性
优点:
简洁直观: 函数名直接表达了其功能,易于理解和使用。
高性能: 底层用 C 实现,经过高度优化,对于简单的子字符串查找,其性能通常优于 `strpos()` 和正则表达式。
现代 PHP 推荐: 如果您的项目运行在 PHP 8.0 或更高版本,这是判断字符串包含的首选方法。
局限性:
PHP 版本要求: 仅适用于 PHP 8.0 及以上版本。对于旧版本 PHP,您需要使用其他方法。
大小写敏感: 默认情况下是大小写敏感的。如果您需要进行大小写不敏感的查找,需要先将字符串转换为统一大小写(例如 `strtolower()`),或者使用其他函数。
无法查找复杂模式: 只能查找字面量子字符串,无法处理通配符、字符集等复杂模式匹配。
二、传统且灵活的选择:`strpos()` 和 `stripos()`
在 PHP 8.0 之前,`strpos()` 是判断字符串包含最常用的方法。它不仅可以判断是否存在,还能返回子字符串首次出现的位置。对于大小写不敏感的查找,还有 `stripos()`。
2.1 `strpos()`:大小写敏感的查找
int|false strpos ( string $haystack , string $needle [, int $offset = 0 ] )
参数说明:
`$haystack`:要搜索的主字符串。
`$needle`:要查找的子字符串。
`$offset`:可选参数,从 `$haystack` 的哪个位置开始搜索。
返回值:如果找到 `$needle`,则返回它首次出现的起始位置(整数);如果未找到,则返回 `false`。
2.2 `stripos()`:大小写不敏感的查找
int|false stripos ( string $haystack , string $needle [, int $offset = 0 ] )
参数说明和返回值与 `strpos()` 相同,但它在查找时会忽略大小写。
2.3 示例代码
<?php
$text = "Hello, world! This is a PHP string example.";
// strpos 示例 (大小写敏感)
if (strpos($text, "PHP") !== false) {
echo "<p>strpos: 字符串包含 'PHP'.</p>"; // 输出:strpos: 字符串包含 'PHP'.
} else {
echo "<p>strpos: 字符串不包含 'PHP'.</p>";
}
if (strpos($text, "php") !== false) {
echo "<p>strpos: 字符串包含 'php' (小写).</p>";
} else {
echo "<p>strpos: 字符串不包含 'php' (小写).</p>"; // 输出:strpos: 字符串不包含 'php' (小写).
}
// stripos 示例 (大小写不敏感)
if (stripos($text, "php") !== false) {
echo "<p>stripos: 字符串包含 'php' (小写).</p>"; // 输出:stripos: 字符串包含 'php' (小写).
} else {
echo "<p>stripos: 字符串不包含 'php' (小写).</p>";
}
// 获取位置信息
$pos = strpos($text, "string");
if ($pos !== false) {
echo "<p>'string' 首次出现在位置: " . $pos . "</p>"; // 输出:'string' 首次出现在位置: 26
}
// 注意:不能简单地使用 `==` 或 `!=` 进行判断,因为 0 被认为是 `false`
// 正确的做法是使用 `!== false` 或 `=== false`
$text_start_with = "apple tree";
if (strpos($text_start_with, "apple") !== false) { // "apple" 在位置 0
echo "<p>'apple' 存在于字符串开头.</p>"; // 输出:'apple' 存在于字符串开头.
}
?>
2.4 优点与局限性
优点:
广泛兼容: 适用于所有版本的 PHP。
功能多样: 除了判断是否存在,还能返回子字符串的位置,这对于某些需要进一步处理(如截取、替换)的场景非常有用。
性能良好: 对于简单的子字符串查找,性能非常接近 `str_contains()`(PHP 8+)或仅略低于它。
大小写控制: `strpos()` 和 `stripos()` 分别提供了大小写敏感和不敏感的选项。
局限性:
返回值判断: 必须使用严格比较 `!== false` 来判断是否包含,因为子字符串可能出现在字符串的起始位置(索引 0),而 0 在松散比较中会被认为是 `false`。这增加了代码的复杂性和出错的可能性。
无法查找复杂模式: 与 `str_contains()` 类似,`strpos()` 也只能查找字面量子字符串,不支持正则表达式模式。
2.5 `strstr()` 和 `stristr()`:获取子字符串及之后的部分
`strstr()` 和 `stristr()` 也是用于查找子字符串的函数,但它们的返回值与 `strpos()` 不同。它们返回从 `$needle` 首次出现的位置到 `$haystack` 结尾的所有字符,或者在未找到时返回 `false`。对于仅仅判断是否包含而言,它们不如 `strpos()` 或 `str_contains()` 直观,但在需要获取匹配部分及其后续内容时非常有用。<?php
$email = "user@";
$domain = strstr($email, "@"); // $domain 将是 "@"
echo "<p>邮箱域名: " . $domain . "</p>"; // 输出:邮箱域名: @
if (strstr($email, "example") !== false) {
echo "<p>邮箱包含 'example'.</p>"; // 输出:邮箱包含 'example'.
}
?>
三、终极模式匹配利器:正则表达式 `preg_match()`
当简单的字面量字符串查找无法满足需求时,正则表达式(Regular Expressions, Regex)便登场了。它允许您定义复杂的匹配模式,包括通配符、字符集、量词、边界等,从而实现高度灵活的字符串查找和验证。在 PHP 中,`preg_match()` 是用于执行单个正则表达式匹配的主要函数。
3.1 什么是正则表达式?
正则表达式是一种强大的文本处理工具,它使用一套特殊的字符序列来定义搜索模式。这些模式可以用于匹配、查找、替换字符串中的文本。例如,您可以定义一个模式来查找所有电子邮件地址、电话号码、特定格式的日期或包含特定词语组合的句子。
3.2 `preg_match()` 语法与用法
int|false preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
参数说明:
`$pattern`:要搜索的正则表达式模式。模式需要用定界符(通常是 `/`、`#` 或 `~`)包围。
`$subject`:要搜索的主字符串。
`$matches`:可选参数,一个数组,如果提供了,它将被填充为所有匹配项。`$matches[0]` 将包含完整匹配到的字符串,`$matches[1]` 将包含第一个捕获组的匹配,依此类推。
`$flags`:可选参数,用于控制匹配行为,例如 `PREG_OFFSET_CAPTURE` 可获取匹配项的偏移量。
`$offset`:可选参数,从 `$subject` 的哪个位置开始搜索。
返回值:如果匹配成功,返回 `1`;如果没有匹配成功,返回 `0`;如果发生错误,返回 `false`。
3.3 基础正则表达式模式
在 PHP 中使用 `preg_match()` 时,模式字符串必须用定界符包裹。例如,`/pattern/`。<?php
$text = "Hello, world! This is a PHP string example.";
// 1. 简单字符串匹配 (与 str_contains 类似)
if (preg_match("/PHP/", $text)) {
echo "<p>正则匹配: 字符串包含 'PHP'.</p>"; // 输出:正则匹配: 字符串包含 'PHP'.
}
// 2. 大小写不敏感匹配 (使用 'i' 修饰符)
if (preg_match("/php/i", $text)) { // 'i' 表示忽略大小写
echo "<p>正则匹配 (不区分大小写): 字符串包含 'php'.</p>"; // 输出:正则匹配 (不区分大小写): 字符串包含 'php'.
}
// 3. 匹配数字
$phone_number = "My phone number is 123-456-7890.";
if (preg_match("/\d{3}-\d{3}-\d{4}/", $phone_number)) { // \d 表示数字,{n} 表示重复n次
echo "<p>正则匹配: 字符串包含电话号码.</p>"; // 输出:正则匹配: 字符串包含电话号码.
}
// 4. 匹配单词边界 (确保匹配的是一个完整的单词,而不是子字符串)
$sentence = "The word is 'cat', not 'category'.";
if (preg_match("/\bcat\b/", $sentence)) { // \b 表示单词边界
echo "<p>正则匹配: 字符串包含完整单词 'cat'.</p>"; // 输出:正则匹配: 字符串包含完整单词 'cat'.
}
// 5. 匹配任意字符 (使用 '.' 通配符)
if (preg_match("//", $text)) { // '.' 匹配除了换行符以外的任意一个字符
echo "<p>正则匹配: 字符串包含 '' 模式 (如 Hello).</p>"; // 输出:正则匹配: 字符串包含 '' 模式 (如 Hello).
}
// 6. 匹配多个可能的字符串 (使用 '|' 或运算符)
if (preg_match("/(apple|banana|orange)/", "I like an orange.")) {
echo "<p>正则匹配: 字符串包含 'apple', 'banana' 或 'orange' 之一.</p>"; // 输出:正则匹配: 字符串包含 'apple', 'banana' 或 'orange' 之一.
}
// 7. 获取匹配结果到 $matches 数组
$url = "/page?id=123";
if (preg_match("#^(https?://)([a-zA-Z0-9\.-]+)(\..*)$#", $url, $matches)) {
echo "<p>正则匹配成功,获取到信息:</p>";
echo "<pre>";
print_r($matches);
echo "</pre>";
/*
输出:
Array
(
[0] => /page?id=123
[1] =>
[2] =>
[3] => .com/page?id=123
)
*/
} else {
echo "<p>正则匹配失败.</p>";
}
?>
3.4 常用正则表达式元字符与修饰符
常用元字符:
`.`:匹配除换行符以外的任意单个字符。
`*`:匹配前一个字符零次或多次。
`+`:匹配前一个字符一次或多次。
`?`:匹配前一个字符零次或一次(也用于非贪婪匹配)。
`^`:匹配字符串的开始。
`$`:匹配字符串的结束。
`\d`:匹配任意数字(等价于 `[0-9]`)。
`\D`:匹配任意非数字字符。
`\w`:匹配任意字母、数字或下划线(等价于 `[a-zA-Z0-9_]`)。
`\W`:匹配任意非字母、数字或下划线字符。
`\s`:匹配任意空白字符(空格、制表符、换行符等)。
`\S`:匹配任意非空白字符。
`\b`:匹配单词边界。
`[abc]`:匹配方括号中的任意一个字符。
`[^abc]`:匹配除方括号中的任意字符。
`(pattern)`:捕获组,将匹配到的内容作为一个单独的结果返回。
`|`:或运算符,匹配 `|` 前或后的模式。
`{n}`:匹配前一个字符恰好 `n` 次。
`{n,}`:匹配前一个字符至少 `n` 次。
`{n,m}`:匹配前一个字符 `n` 到 `m` 次。
常用修饰符:
`i`:不区分大小写匹配(case-insensitive)。
`m`:多行模式。`^` 和 `$` 不仅匹配字符串的开始和结束,也匹配每一行的开始和结束。
`s`:点号 `.` 匹配所有字符,包括换行符。
`u`:UTF-8 匹配。确保正则表达式正确处理多字节字符,对于包含中文或其他非 ASCII 字符的字符串非常重要。
3.5 优点与局限性
优点:
极度灵活: 能够匹配几乎任何复杂的字符串模式,实现强大的搜索、验证和解析功能。
模式抽象: 可以用简洁的模式表达复杂的匹配逻辑,提高代码的表达力。
捕获组: 能够捕获匹配模式中的特定部分,方便进一步提取和处理数据。
局限性:
性能开销: 相对于 `str_contains()` 和 `strpos()`,正则表达式的匹配过程通常涉及更复杂的算法,因此在简单的字面量查找上,性能会更低。
学习曲线: 正则表达式语法复杂且不直观,需要一定的学习和实践才能掌握。
可读性: 复杂的正则表达式模式可能难以理解和维护,容易出错。
安全风险: 不当的正则表达式模式可能导致回溯失控(catastrophic backtracking),造成性能急剧下降甚至拒绝服务(DoS)攻击。
四、选择哪种方法?性能与场景分析
面对多种选择,关键在于根据具体的场景和需求,选择“正确”的工具。以下是一些指导原则:
最简单、最快:`str_contains()` (PHP 8+)
场景: 您只需要判断一个字符串是否包含另一个字面量子字符串,且不关心大小写或位置。这是 PHP 8+ 项目的首选。
优点: 代码最简洁,性能最高。
示例: 检查用户输入是否包含敏感词、URL 中是否包含特定参数。
传统、兼顾功能:`strpos()` / `stripos()`
场景:
项目运行在 PHP 7.x 或更早版本,无法使用 `str_contains()`。
您不仅需要判断是否存在,还需要获取子字符串的位置,以便进行进一步的字符串操作(如 `substr()`)。
需要进行大小写敏感或不敏感的字面量子字符串查找。
优点: 性能接近 `str_contains()`,功能比它更丰富(提供位置信息),兼容性好。
注意事项: 务必使用 `!== false` 进行严格判断。
示例: 从 URL 中提取文件名、检查文章中是否存在某个关键词并高亮。
复杂模式、高级匹配:`preg_match()`
场景:
需要匹配非字面量的复杂模式,如电子邮件格式、电话号码、日期格式、URL 结构等。
需要使用通配符、字符集、量词等高级匹配规则。
需要从字符串中提取符合特定模式的多个部分(使用捕获组)。
优点: 功能最强大,能够处理最复杂的字符串匹配需求。
注意事项:
对于简单需求避免滥用,因为性能开销较大。
仔细构建正则表达式,防止回溯失控,影响性能。
注意使用 `u` 修饰符处理 UTF-8 编码的字符串。
示例: 验证用户输入的邮箱、提取日志文件中的错误代码、解析特定格式的文本数据。
性能对比(概括性)
在绝大多数情况下,对于简单的字面量子字符串查找:
`str_contains()` (PHP 8+) > `strpos()` / `stripos()` > `preg_match()`
这种性能差异在处理小字符串或少量操作时可能不明显,但在处理大量数据或长字符串时,选择合适的函数能显著提升程序效率。
五、最佳实践与常见陷阱
优先使用专用字符串函数: 在能用 `str_contains()` 或 `strpos()` 解决问题时,尽量不要使用正则表达式。它们更简单、更快、更易读。
严格判断 `strpos()` 的返回值: 永远使用 `!== false` 或 `=== false` 来判断 `strpos()` 或 `stripos()` 的结果。
处理正则表达式中的特殊字符: 如果你的 `$needle` 字符串本身包含正则表达式的特殊字符(如 `.`, `*`, `+`, `?`, `(` `)` `[` `]` `{` `}` `\` `|` `^` `$`), 并且你希望它们被字面量匹配,你需要使用 `preg_quote()` 函数对其进行转义。
<?php
$text = "What is the price of $10.00?";
$search_pattern = "$10.00";
$escaped_pattern = preg_quote($search_pattern, '/'); // 第二个参数是定界符
if (preg_match("/" . $escaped_pattern . "/", $text)) {
echo "<p>正则匹配: 字符串包含 '$10.00'.</p>";
}
?>
注意 UTF-8 编码: 如果你的字符串包含多字节字符(如中文),在使用 `preg_match()` 时务必添加 `u` 修饰符(例如 `/pattern/u`),以确保正则表达式能够正确处理这些字符。对于 `str_contains()` 和 `strpos()`,它们通常能正确处理 UTF-8,但如果需要更高级的多字节字符串处理,可以考虑 `mb_strpos()` 等 `mb_` 系列函数。
测试你的正则表达式: 使用在线正则表达式测试工具(如 , )来构建和测试你的模式,确保它们按预期工作。
避免正则表达式回溯失控: 避免使用过多的嵌套量词(如 `(a+)*`)或在长字符串上应用过于复杂的贪婪匹配模式,这可能导致性能问题。
PHP 提供了多种强大而灵活的方式来判断字符串中是否包含特定子字符串。对于现代 PHP 项目(PHP 8.0+),`str_contains()` 是处理字面量子字符串查找最简洁、最高效的选择。对于需要兼容旧版本 PHP 或需要获取子字符串位置的场景,`strpos()` 和 `stripos()` 依然是优秀的解决方案。而当面临复杂模式匹配、需要通配符和高级逻辑时,正则表达式及其 `preg_match()` 函数则成为不可替代的利器。
作为专业的程序员,我们不仅要熟悉这些工具的用法,更要理解它们的底层机制、性能特点和适用场景。通过明智地选择合适的函数,我们能够编写出更高效、更健壮、更易于维护的 PHP 代码,从而更好地应对各种字符串处理挑战。
2025-10-15

PHP字符串字符出现次数统计:从基础函数到高级应用与性能优化
https://www.shuihudhg.cn/129540.html

C语言printf性能深度解析:慢在哪?如何优化?
https://www.shuihudhg.cn/129539.html

Java代码可视化:从截图到智能分享的最佳实践与工具
https://www.shuihudhg.cn/129538.html

Java网校代码实战指南:零基础到企业级开发的进阶之路
https://www.shuihudhg.cn/129537.html

Java 进程管理:从启动到监控的深度实践
https://www.shuihudhg.cn/129536.html
热门文章

在 PHP 中有效获取关键词
https://www.shuihudhg.cn/19217.html

PHP 对象转换成数组的全面指南
https://www.shuihudhg.cn/75.html

PHP如何获取图片后缀
https://www.shuihudhg.cn/3070.html

将 PHP 字符串转换为整数
https://www.shuihudhg.cn/2852.html

PHP 连接数据库字符串:轻松建立数据库连接
https://www.shuihudhg.cn/1267.html