PHP substr() 字串擷取

substr() 函數用來擷取字串的一部分。

基本用法

<?php
$str = "Hello World";

echo substr($str, 0, 5);   // "Hello"
echo substr($str, 6);      // "World"
?>

語法

substr(string $string, int $offset, ?int $length = null): string
  • $string:原始字串
  • $offset:開始位置(從 0 開始)
  • $length:要擷取的長度(可選)
  • 回傳值:擷取的子字串,失敗時回傳空字串(PHP 8)或 false(PHP 7)

使用正數 offset

<?php
$str = "Hello World";
//      01234567890

echo substr($str, 0);      // "Hello World"(從頭開始)
echo substr($str, 1);      // "ello World"
echo substr($str, 6);      // "World"
echo substr($str, 0, 5);   // "Hello"
echo substr($str, 6, 5);   // "World"
echo substr($str, 0, 1);   // "H"
?>

使用負數 offset

負數 offset 從字串尾端算起:

<?php
$str = "Hello World";

echo substr($str, -5);     // "World"(從倒數第 5 個開始)
echo substr($str, -5, 3);  // "Wor"
echo substr($str, -1);     // "d"(最後一個字元)
?>

使用負數 length

負數 length 表示要排除尾端的字元數:

<?php
$str = "Hello World";

echo substr($str, 0, -1);   // "Hello Worl"(排除最後 1 個字元)
echo substr($str, 0, -6);   // "Hello"(排除最後 6 個字元)
echo substr($str, 2, -2);   // "llo Wor"
?>

組合使用

<?php
$str = "Hello World";

// 從倒數第 5 個開始,擷取到倒數第 1 個(不含)
echo substr($str, -5, -1);  // "Worl"

// 從第 2 個開始,擷取到倒數第 3 個(不含)
echo substr($str, 2, -3);   // "llo Wo"
?>

邊界情況

<?php
$str = "Hello";

// offset 超過字串長度
echo substr($str, 10);  // ""(PHP 8)或 false(PHP 7)

// length 超過剩餘長度
echo substr($str, 2, 100);  // "llo"(自動調整)

// 空字串
echo substr("", 0, 5);  // ""
?>

中文字串使用 mb_substr()

substr() 以位元組為單位,處理中文需要使用 mb_substr()

<?php
$str = "你好世界";

// substr 會切斷中文字
echo substr($str, 0, 3);     // "你"(剛好 3 bytes)
echo substr($str, 0, 4);     // 亂碼(切斷了一個中文字)

// mb_substr 以字元為單位
echo mb_substr($str, 0, 2);  // "你好"
echo mb_substr($str, 2);     // "世界"
echo mb_substr($str, -1);    // "界"
?>

常見應用

取得檔案副檔名

<?php
$filename = "document.pdf";

// 使用 substr
$ext = substr($filename, strrpos($filename, '.') + 1);
echo $ext;  // "pdf"

// 更簡潔的方式
$ext = pathinfo($filename, PATHINFO_EXTENSION);
?>

隱藏部分資訊

<?php
function maskEmail(string $email): string {
    $atPos = strpos($email, '@');
    if ($atPos === false || $atPos < 2) {
        return $email;
    }
    
    $firstChar = substr($email, 0, 1);
    $domain = substr($email, $atPos);
    $maskedLength = $atPos - 1;
    
    return $firstChar . str_repeat('*', $maskedLength) . $domain;
}

echo maskEmail("alice@example.com");  // a****@example.com

function maskPhone(string $phone): string {
    return substr($phone, 0, 4) . '****' . substr($phone, -2);
}

echo maskPhone("0912345678");  // 0912****78
?>

截斷文字

<?php
function truncate(string $text, int $length = 100, string $suffix = '...'): string {
    if (mb_strlen($text) <= $length) {
        return $text;
    }
    return mb_substr($text, 0, $length) . $suffix;
}

echo truncate("這是一段很長的文字", 5);  // 這是一段很...
?>

取得子字串

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

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

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

格式化字串

<?php
// 信用卡號格式化
function formatCreditCard(string $number): string {
    $number = preg_replace('/\D/', '', $number);
    return substr($number, 0, 4) . ' ' . 
           substr($number, 4, 4) . ' ' . 
           substr($number, 8, 4) . ' ' . 
           substr($number, 12, 4);
}

echo formatCreditCard("1234567890123456");  // 1234 5678 9012 3456
?>

與其他函數比較

函數說明
substr()擷取子字串(位元組)
mb_substr()擷取子字串(字元)
substr_replace()取代子字串
strstr()取得第一次出現後的部分
<?php
$str = "Hello World";

echo substr($str, 6);           // "World"
echo strstr($str, " ");         // " World"(包含分隔符)
echo strstr($str, " ", true);   // "Hello"(分隔符之前)
?>

效能考量

substr() 會建立新字串,不會修改原字串:

<?php
$str = "Hello World";
$sub = substr($str, 0, 5);

echo $str;  // "Hello World"(原字串不變)
echo $sub;  // "Hello"
?>