PHP 数组与字符串内容查找:从基础到高效,全面解析与最佳实践58
在日常的 PHP 开发中,判断一个字符串是否包含另一个子字符串,或者一个数组中是否包含某个特定的元素或包含某个子字符串,是极其常见的需求。这些操作看似简单,但根据不同的场景、性能要求以及 PHP 版本,选择最合适的函数和方法至关重要。本文将作为一份详尽的指南,深入探讨 PHP 中处理这类“包含”判断的各种技术,从基础函数到高级技巧,再到性能优化和最佳实践,旨在帮助您写出更健壮、更高效的代码。
一、 字符串中查找字符串(String Contains String)
判断一个字符串(`haystack`,干草堆)中是否包含另一个子字符串(`needle`,针),是字符串操作中最基本的需求之一。PHP 提供了多种函数来完成这项任务,它们的区别主要在于返回值、性能以及是否区分大小写。
1.1 PHP 8+:使用 `str_contains()`
PHP 8.0 引入了一个全新的函数 `str_contains()`,它专门用于判断字符串是否包含子字符串,语义清晰,用法直观,是现代 PHP 开发的首选。
<?php
$text = "Hello, world! PHP is awesome.";
$needle1 = "PHP";
$needle2 = "Java";
$needle3 = "hello"; // 注意大小写
if (str_contains($text, $needle1)) {
echo "<p>'$text' 包含 '$needle1'</p>"; // 输出:'Hello, world! PHP is awesome.' 包含 'PHP'
}
if (!str_contains($text, $needle2)) {
echo "<p>'$text' 不包含 '$needle2'</p>"; // 输出:'Hello, world! PHP is awesome.' 不包含 'Java'
}
if (!str_contains($text, $needle3)) {
echo "<p>'$text' 不包含 '$needle3' (区分大小写)</p>"; // 输出:'Hello, world! PHP is awesome.' 不包含 'hello' (区分大小写)
}
?>
特点:
返回值:布尔值 `true` 或 `false`。
大小写敏感:默认区分大小写。
性能:对于简单查找,性能非常高,通常优于 `strpos()`。
易用性:语义最清晰,推荐在 PHP 8+ 项目中使用。
1.2 经典方法:`strpos()` 和 `stripos()`
在 PHP 8.0 之前,`strpos()` 是最常用的字符串查找函数。它返回子字符串首次出现的位置,如果没有找到则返回 `false`。`stripos()` 是其不区分大小写的版本。
<?php
$text = "Hello, world! PHP is awesome.";
$needle1 = "PHP";
$needle2 = "Java";
$needle3 = "hello";
// strpos() - 区分大小写
if (strpos($text, $needle1) !== false) {
echo "<p>'$text' 包含 '$needle1'</p>"; // 输出:'Hello, world! PHP is awesome.' 包含 'PHP'
}
if (strpos($text, $needle2) === false) {
echo "<p>'$text' 不包含 '$needle2'</p>"; // 输出:'Hello, world! PHP is awesome.' 不包含 'Java'
}
if (strpos($text, $needle3) === false) {
echo "<p>'$text' 不包含 '$needle3' (区分大小写)</p>"; // 输出:'Hello, world! PHP is awesome.' 不包含 'hello' (区分大小写)
}
// stripos() - 不区分大小写
if (stripos($text, $needle3) !== false) {
echo "<p>'$text' 包含 '$needle3' (不区分大小写)</p>"; // 输出:'Hello, world! PHP is awesome.' 包含 'hello' (不区分大小写)
}
?>
重要提示: `strpos()` 在子字符串位于源字符串开头时,会返回 `0`。由于 `0` 在布尔上下文中会被转换为 `false`,因此必须使用严格比较运算符 `!== false` 或 `=== false` 来判断,否则可能会出现逻辑错误。
特点:
返回值:成功找到返回子字符串首次出现的位置(从 `0` 开始),未找到返回 `false`。
大小写敏感:`strpos()` 区分大小写,`stripos()` 不区分大小写。
性能:对于简单查找,性能非常好,仅次于 `str_contains()`。
兼容性:适用于所有 PHP 版本。
1.3 获取子字符串或其之前/之后的部分:`strstr()` 和 `stristr()`
`strstr()` 函数用于查找子字符串的首次出现,并返回从该子字符串开始到源字符串结尾的部分。如果只需要判断是否存在,它也可以间接实现。
<?php
$email = "user@";
$domain = strstr($email, '@'); // $domain 会是 "@"
if ($domain !== false) {
echo "<p>Email '$email' 包含 '@' 符号。</p>"; // 输出:Email 'user@' 包含 '@' 符号。
echo "<p>域名部分: " . substr($domain, 1) . "</p>"; // 输出:域名部分:
}
$url = "/path/to/";
$file_path = strstr($url, '/', true); // 传递 true 作为第三个参数,返回 needle 之前的部分
echo "<p>协议部分: " . $file_path . "</p>"; // 输出:协议部分: http:
// stristr() - 不区分大小写
$text = "PHP is fun!";
$find = "php";
if (stristr($text, $find) !== false) {
echo "<p>'$text' (不区分大小写) 包含 '$find'</p>"; // 输出:'PHP is fun!' (不区分大小写) 包含 'php'
}
?>
特点:
返回值:成功找到返回子字符串及其之后的部分(或之前的部分,如果第三个参数为 `true`),未找到返回 `false`。
大小写敏感:`strstr()` 区分大小写,`stristr()` 不区分大小写。
使用场景:当不仅需要判断是否存在,还需要提取包含子字符串的特定部分时,此函数非常有用。
1.4 强大的模式匹配:`preg_match()`
当需要进行更复杂的模式匹配时,正则表达式是不可或缺的工具。`preg_match()` 函数可以检查一个字符串是否匹配某个正则表达式模式。
<?php
$text = "PHP is a scripting language, version 8.2.";
$pattern1 = "/PHP/"; // 查找 "PHP"
$pattern2 = "/\d+\.\d+/"; // 查找版本号,如 "8.2"
$pattern3 = "/php/i"; // 不区分大小写查找 "php"
if (preg_match($pattern1, $text)) {
echo "<p>'$text' 匹配模式 '$pattern1'</p>"; // 输出:'PHP is a scripting language, version 8.2.' 匹配模式 '/PHP/'
}
if (preg_match($pattern2, $text, $matches)) {
echo "<p>'$text' 匹配模式 '$pattern2',找到: " . $matches[0] . "</p>"; // 输出:'PHP is a scripting language, version 8.2.' 匹配模式 '/\d+\.\d+/',找到: 8.2
}
if (preg_match($pattern3, $text)) {
echo "<p>'$text' (不区分大小写) 匹配模式 '$pattern3'</p>"; // 输出:'PHP is a scripting language, version 8.2.' (不区分大小写) 匹配模式 '/php/i'
}
?>
特点:
返回值:如果匹配成功返回 `1`,未找到返回 `0`,发生错误返回 `false`。
大小写敏感:默认区分大小写,可以通过添加 `i` 修饰符实现不区分大小写。
使用场景:适用于复杂的模式匹配、验证输入等。
性能:正则表达式引擎通常比简单的字符串查找函数开销更大,如果只是简单的子字符串查找,不推荐使用 `preg_match()`。
1.5 字符串查找方法总结与选择建议
| 函数/方法 | PHP 版本 | 大小写敏感 | 返回值 | 适用场景 | 性能 |
| :-------------------- | :------- | :--------- | :----------------------------------- | :------------------------------------------------- | :------- |
| `str_contains()` | 8.0+ | 是 | `true`/`false` | 最简单、语义最清晰的判断是否存在 | 最佳 |
| `strpos()` | 所有 | 是 | 首次出现位置 (int) / `false` | 判断是否存在,或需要知道位置 | 优秀 |
| `stripos()` | 所有 | 否 | 首次出现位置 (int) / `false` | 不区分大小写判断是否存在,或需要知道位置 | 优秀 |
| `strstr()` / `stristr()` | 所有 | 是/否 | 子字符串及之后(或之前)的部分 / `false` | 判断是否存在,并需要获取子字符串相关部分 | 一般 |
| `preg_match()` | 所有 | 可配置 | `1`/`0`/`false` | 复杂模式匹配、多条件查找、验证输入 | 较差 |
选择建议:
PHP 8+ 且只判断是否存在:首选 `str_contains()`。
PHP 7.x 及更早版本,或需要获取位置:使用 `strpos()` / `stripos()` (注意 `!== false` 判断)。
需要获取匹配部分的字符串:考虑 `strstr()` / `stristr()`。
需要进行复杂模式匹配(例如多个关键字、特定格式等):使用 `preg_match()`。
二、 数组中查找字符串(Array Contains String)
在数组中查找字符串有两种主要情景:一是查找数组中是否存在一个完全匹配的元素;二是查找数组中是否有元素包含某个子字符串。
2.1 查找数组元素是否完全匹配(Exact Match)
当我们需要判断数组中是否存在一个与目标字符串完全相等的元素时,`in_array()` 是最直接和高效的方法。
<?php
$fruits = ["apple", "banana", "orange", "grape"];
$searchFruit1 = "banana";
$searchFruit2 = "kiwi";
$searchFruit3 = "Banana"; // 注意大小写
// 默认非严格比较
if (in_array($searchFruit1, $fruits)) {
echo "<p>数组中包含 '$searchFruit1'</p>"; // 输出:数组中包含 'banana'
}
if (!in_array($searchFruit2, $fruits)) {
echo "<p>数组中不包含 '$searchFruit2'</p>"; // 输出:数组中不包含 'kiwi'
}
// 严格比较 (第三个参数设为 true)
if (!in_array($searchFruit3, $fruits, true)) {
echo "<p>数组中不包含 '$searchFruit3' (严格区分大小写)</p>"; // 输出:数组中不包含 'Banana' (严格区分大小写)
}
$numbers = [1, "2", 3];
if (in_array("1", $numbers)) {
echo "<p>数组中包含 '1' (非严格比较)</p>"; // 输出:数组中包含 '1' (非严格比较)
}
if (!in_array("1", $numbers, true)) {
echo "<p>数组中不包含 '1' (严格比较)</p>"; // 输出:数组中不包含 '1' (严格比较)
}
?>
特点:
返回值:布尔值 `true` 或 `false`。
严格比较:第三个参数 `$strict` 默认为 `false`(非严格比较,即 `==`),如果设置为 `true`,则会进行严格比较(`===`),同时检查类型和值。这对于避免意外的类型转换非常重要。
性能:对于大多数情况,`in_array()` 已经非常优化,但在处理超大型数组时,如果元素是字符串且需要频繁查询,可以考虑将数组值作为键构建一个关联数组进行 `isset()` 查找,性能会更高(O(1) vs O(N))。
2.2 查找数组元素是否包含某个子字符串(Substring within Elements)
这比查找完全匹配更复杂,因为需要遍历数组的每个元素,并对每个元素执行字符串查找。PHP 没有直接的内置函数来完成这项任务,但可以通过组合其他函数来实现。
2.2.1 使用 `foreach` 循环 + 字符串查找函数
这是最直接、最易懂的方法。遍历数组,对每个元素使用 `str_contains()` (PHP 8+) 或 `strpos()` 进行判断。一旦找到匹配项,就可以提前退出循环以优化性能。
<?php
$sentences = [
"PHP is a server-side scripting language.",
"Python is popular for data science.",
"JavaScript runs in the browser.",
"Learning PHP is fun!"
];
$searchKeyword = "PHP";
$found = false;
foreach ($sentences as $sentence) {
// 使用 str_contains (PHP 8+)
if (str_contains($sentence, $searchKeyword)) {
$found = true;
break; // 找到即退出,提高效率
}
// PHP 7.x 及以下可以使用 strpos
// if (strpos($sentence, $searchKeyword) !== false) {
// $found = true;
// break;
// }
}
if ($found) {
echo "<p>数组中至少有一个句子包含 '$searchKeyword'</p>"; // 输出:数组中至少有一个句子包含 'PHP'
} else {
echo "<p>数组中没有句子包含 '$searchKeyword'</p>";
}
// 不区分大小写的查找
$searchKeywordLower = "php";
$foundCaseInsensitive = false;
foreach ($sentences as $sentence) {
if (stripos($sentence, $searchKeywordLower) !== false) {
$foundCaseInsensitive = true;
break;
}
}
if ($foundCaseInsensitive) {
echo "<p>数组中至少有一个句子包含 '$searchKeywordLower' (不区分大小写)</p>"; // 输出:数组中至少有一个句子包含 'php' (不区分大小写)
}
?>
特点:
灵活性:可以自由选择字符串查找函数 (`str_contains`, `strpos`, `stripos`, `preg_match`)。
控制性:可以实现找到第一个匹配项后立即停止遍历。
性能:对于只需要布尔结果的情况,效率很高,尤其是在匹配项靠前时。
2.2.2 使用 `array_filter()` 过滤数组
如果需要找出所有包含特定子字符串的数组元素,`array_filter()` 是一个非常优雅的函数式编程方法。它会遍历数组中的每个元素,并将其传递给回调函数。如果回调函数返回 `true`,该元素会被保留在结果数组中。
<?php
$products = [
"Laptop Pro X",
"Monitor Ultra HD",
"Gaming Keyboard RGB",
"Laptop Sleeve 15-inch"
];
$filterKeyword = "Laptop";
$filteredProducts = array_filter($products, function($product) use ($filterKeyword) {
// PHP 8+
return str_contains($product, $filterKeyword);
// PHP 7.x 及以下
// return strpos($product, $filterKeyword) !== false;
});
echo "<p>包含 '$filterKeyword' 的产品有:</p>";
if (!empty($filteredProducts)) {
echo "<ul>";
foreach ($filteredProducts as $product) {
echo "<li>" . htmlspecialchars($product) . "</li>";
}
echo "</ul>";
} else {
echo "<p>无</p>";
}
// 不区分大小写的过滤
$filterKeywordLower = "hd";
$filteredProductsLower = array_filter($products, function($product) use ($filterKeywordLower) {
return stripos($product, $filterKeywordLower) !== false;
});
echo "<p>包含 '$filterKeywordLower' (不区分大小写) 的产品有:</p>";
if (!empty($filteredProductsLower)) {
echo "<ul>";
foreach ($filteredProductsLower as $product) {
echo "<li>" . htmlspecialchars($product) . "</li>";
}
echo "</ul>";
} else {
echo "<p>无</p>";
}
// 如果只需要判断是否存在,可以检查 array_filter 结果是否为空
if (!empty($filteredProducts)) {
echo "<p>数组中存在包含 '$filterKeyword' 的元素。</p>";
}
?>
特点:
简洁性:代码更紧凑,尤其适用于获取所有匹配项。
返回值:返回一个新的数组,包含所有符合条件的元素。
性能:需要遍历整个数组,无法像 `foreach` 那样提前退出。如果只关注是否存在,性能可能略逊于带 `break` 的 `foreach` 循环。
2.2.3 使用 `preg_grep()` 进行正则表达式过滤
`preg_grep()` 函数类似于 `array_filter()`,但它专门用于通过正则表达式模式过滤数组元素。当需要基于复杂模式查找时非常有用。
<?php
$names = ["Alice Smith", "Bob Johnson", "Charlie Brown", "David Miller"];
// 查找姓氏以 "S" 或 "B" 开头的人
$pattern = "/ (S|B)\w+/"; // 注意空格
$matchedNames = preg_grep($pattern, $names);
echo "<p>匹配模式 '$pattern' 的名字有:</p>";
if (!empty($matchedNames)) {
echo "<ul>";
foreach ($matchedNames as $name) {
echo "<li>" . htmlspecialchars($name) . "</li>"; // 输出:Alice Smith, Charlie Brown
}
echo "</ul>";
} else {
echo "<p>无</p>";
}
// 查找包含数字的元素
$items = ["item-1", "item-2", "product-A", "product-B"];
$pattern_digits = "/\d/";
$itemsWithDigits = preg_grep($pattern_digits, $items);
echo "<p>包含数字的元素有:</p>";
if (!empty($itemsWithDigits)) {
echo "<ul>";
foreach ($itemsWithDigits as $item) {
echo "<li>" . htmlspecialchars($item) . "</li>"; // 输出:item-1, item-2
}
echo "</ul>";
} else {
echo "<p>无</p>";
}
?>
特点:
强大:能够处理复杂的模式匹配需求。
返回值:返回一个包含所有匹配元素的数组。
性能:正则表达式开销较大,仅在简单字符串查找无法满足需求时考虑使用。
三、 性能优化与最佳实践
选择正确的工具对于性能和代码可读性都至关重要。以下是一些通用的最佳实践建议:
3.1 选择最合适的函数
简单布尔判断(字符串):`str_contains()` (PHP 8+) 是首选。如果 PHP 版本低于 8.0,使用 `strpos() !== false`。
需要匹配位置或子字符串:`strpos()` / `stripos()` 用于位置,`strstr()` / `stristr()` 用于获取子字符串。
复杂模式匹配(字符串或数组元素):`preg_match()` 或 `preg_grep()`。
数组完全匹配:`in_array()`。
数组元素包含子字符串:如果只需要布尔结果,使用带 `break` 的 `foreach` 循环。如果需要所有匹配元素,使用 `array_filter()` 或 `preg_grep()`。
3.2 区分大小写的重要性
始终明确你的需求是否区分大小写。选择合适的函数(如 `strpos` vs `stripos`, `str_contains` vs 手动 `strtolower` 后再查找)或正则修饰符 (`i`)。
3.3 PHP 版本兼容性
对于 `str_contains()` 等 PHP 8.0+ 的新函数,如果你的项目需要兼容旧版本 PHP,可能需要实现一个兼容函数(polyfill)或使用其他兼容旧版本的函数。
<?php
// str_contains() polyfill for older PHP versions
if (!function_exists('str_contains')) {
function str_contains(string $haystack, string $needle): bool
{
return '' === $needle || false !== strpos($haystack, $needle);
}
}
?>
3.4 处理大数据量
数组中的精确查找:如果需要对非常大的数组进行频繁的精确值查找,并且数组的值是唯一的,可以考虑将其转换为键值对(通过 `array_flip()`),然后使用 `isset()` 来进行 O(1) 查找,而不是 `in_array()` 的 O(N) 查找。
<?php
$largeArray = ['itemA', 'itemB', 'itemC', /* ... 100000+ items ... */];
$flippedArray = array_flip($largeArray); // 值为键
if (isset($flippedArray['itemX'])) {
// Found
}
?>
尽早退出:在 `foreach` 循环中,一旦找到所需内容,立即使用 `break` 语句退出循环,避免不必要的迭代。
避免不必要的函数调用:例如,不要在循环内部重复定义回调函数,或者重复编译正则表达式。
3.5 可读性与维护性
虽然性能很重要,但代码的可读性和维护性也同样关键。选择语义最清晰的函数,并添加必要的注释,尤其是在使用复杂正则表达式或进行性能优化时。
PHP 提供了丰富而灵活的函数来应对字符串和数组中的内容查找需求。从 PHP 8.0 的 `str_contains()` 到经典的 `strpos()`,再到功能强大的 `preg_match()` 和 `array_filter()`,每种方法都有其特定的最佳使用场景。
作为专业的程序员,我们应该熟悉这些工具的特点、性能考量和适用范围。通过明智地选择和组合这些函数,并在处理大数据量时采取适当的优化策略,我们可以编写出既高效又易于维护的 PHP 代码,从而更好地应对各种复杂的开发挑战。
2025-11-22
C语言精妙表格输出:从基础到高级实践与技巧
https://www.shuihudhg.cn/133366.html
PHP 数组与字符串内容查找:从基础到高效,全面解析与最佳实践
https://www.shuihudhg.cn/133365.html
Linux环境下PHP文件启动与运行的全面指南
https://www.shuihudhg.cn/133364.html
Python文件读取完全指南:从入门到高效实践
https://www.shuihudhg.cn/133363.html
PHP cURL 深度解析:高效获取与管理HTTP Cookies的策略与实践
https://www.shuihudhg.cn/133362.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