PHP strpos() 字串搜尋

strpos() 函數用來搜尋子字串在字串中第一次出現的位置。

基本用法

<?php
$str = "Hello World";

echo strpos($str, "World");  // 6
echo strpos($str, "o");      // 4
?>

語法

strpos(string $haystack, string $needle, int $offset = 0): int|false
  • $haystack:要搜尋的字串
  • $needle:要尋找的子字串
  • $offset:開始搜尋的位置(可選)
  • 回傳值:找到時回傳位置(從 0 開始),找不到回傳 false

注意:正確檢查結果

<?php
$str = "Hello World";

// 錯誤的判斷方式
if (strpos($str, "Hello")) {  // 位置 0 會被當成 false
    echo "找到了";
} else {
    echo "找不到";  // 輸出:找不到(錯誤!)
}

// 正確的判斷方式
if (strpos($str, "Hello") !== false) {
    echo "找到了";  // 輸出:找到了
}
?>
因為 strpos() 可能回傳 0(在位置 0 找到),一定要用 !== false 來判斷,不能只用 if (strpos(...))

使用 offset 參數

<?php
$str = "Hello Hello Hello";

echo strpos($str, "Hello");       // 0
echo strpos($str, "Hello", 1);    // 6(從位置 1 開始搜尋)
echo strpos($str, "Hello", 7);    // 12
?>

找出所有出現位置

<?php
function findAll(string $haystack, string $needle): array {
    $positions = [];
    $offset = 0;
    
    while (($pos = strpos($haystack, $needle, $offset)) !== false) {
        $positions[] = $pos;
        $offset = $pos + 1;
    }
    
    return $positions;
}

$str = "Hello Hello Hello";
print_r(findAll($str, "Hello"));  // [0, 6, 12]
?>

相關函數

stripos() - 不區分大小寫

<?php
$str = "Hello World";

echo strpos($str, "hello");   // false(找不到)
echo stripos($str, "hello");  // 0(找到)
echo stripos($str, "WORLD");  // 6
?>

strrpos() - 最後一次出現的位置

<?php
$str = "Hello Hello Hello";

echo strpos($str, "Hello");   // 0(第一次)
echo strrpos($str, "Hello");  // 12(最後一次)
?>

strripos() - 不區分大小寫的最後位置

<?php
$str = "Hello HELLO hello";

echo strrpos($str, "Hello");   // 0(區分大小寫)
echo strripos($str, "Hello");  // 12(不區分大小寫)
?>

常見應用

檢查子字串是否存在

<?php
$str = "Hello World";

// 使用 strpos
if (strpos($str, "World") !== false) {
    echo "包含 World";
}

// PHP 8+ 使用 str_contains
if (str_contains($str, "World")) {
    echo "包含 World";
}
?>

驗證 Email 格式(簡易版)

<?php
function hasAtSign(string $email): bool {
    return strpos($email, '@') !== false;
}

var_dump(hasAtSign("user@example.com"));  // true
var_dump(hasAtSign("invalid-email"));     // false
?>

解析 URL

<?php
$url = "https://example.com/path/to/file";

// 取得協定
$protocolEnd = strpos($url, '://');
$protocol = substr($url, 0, $protocolEnd);
echo $protocol;  // "https"

// 取得域名
$domainStart = $protocolEnd + 3;
$domainEnd = strpos($url, '/', $domainStart);
$domain = substr($url, $domainStart, $domainEnd - $domainStart);
echo $domain;  // "example.com"
?>

取得檔案副檔名

<?php
function getExtension(string $filename): string {
    $pos = strrpos($filename, '.');
    
    if ($pos === false) {
        return '';
    }
    
    return substr($filename, $pos + 1);
}

echo getExtension("document.pdf");      // "pdf"
echo getExtension("archive.tar.gz");    // "gz"
echo getExtension("no-extension");      // ""
?>

關鍵字高亮

<?php
function highlight(string $text, string $keyword): string {
    $pos = stripos($text, $keyword);
    
    if ($pos === false) {
        return $text;
    }
    
    $before = substr($text, 0, $pos);
    $match = substr($text, $pos, strlen($keyword));
    $after = substr($text, $pos + strlen($keyword));
    
    return $before . "<mark>$match</mark>" . $after;
}

echo highlight("Hello World", "world");
// Hello <mark>World</mark>
?>

計算子字串出現次數

<?php
function countOccurrences(string $haystack, string $needle): int {
    $count = 0;
    $offset = 0;
    
    while (($pos = strpos($haystack, $needle, $offset)) !== false) {
        $count++;
        $offset = $pos + 1;
    }
    
    return $count;
}

echo countOccurrences("Hello Hello Hello", "Hello");  // 3

// 使用內建函數
echo substr_count("Hello Hello Hello", "Hello");  // 3
?>

PHP 8+ 新函數

str_contains() - 檢查是否包含

<?php
$str = "Hello World";

// 舊寫法
if (strpos($str, "World") !== false) { }

// PHP 8+
if (str_contains($str, "World")) { }
?>

str_starts_with() - 檢查開頭

<?php
$str = "Hello World";

// 舊寫法
if (strpos($str, "Hello") === 0) { }

// PHP 8+
if (str_starts_with($str, "Hello")) { }
?>

str_ends_with() - 檢查結尾

<?php
$str = "Hello World";

// 舊寫法
if (substr($str, -5) === "World") { }

// PHP 8+
if (str_ends_with($str, "World")) { }
?>

效能考量

<?php
$longString = str_repeat("Hello World ", 10000);

// strpos 在找到後會立即停止,效率高
$start = microtime(true);
$pos = strpos($longString, "World");
$time1 = microtime(true) - $start;

// 正規表示式通常較慢
$start = microtime(true);
preg_match('/World/', $longString, $matches);
$time2 = microtime(true) - $start;

// strpos 通常比 preg_match 快
?>