6.1 字符串基础
字符串是PHP编程中最基本也是最常用的数据类型之一。理解字符串的基础知识是掌握PHP字符串处理的第一步。本节将详细介绍字符串的创建、表示、访问、修改等基本操作。
字符串的定义和概念
什么是字符串?
字符串是由零个或多个字符组成的有限序列。在PHP中,字符串可以包含:
- 字母(大小写英文字母、中文字符等)
- 数字(0-9)
- 特殊符号(!@#$%^&*()等)
- 空格和空白字符
- 控制字符(换行符、制表符等)
字符串在内存中的表示
PHP将字符串存储为字节序列,这意味着:
<?php
// 不同编码的字符串
$ascii_string = "Hello"; // ASCII编码,每个字符占1字节
$utf8_chinese = "你好"; // UTF-8编码,中文字符通常占3字节
$emoji = "😊"; // UTF-8编码,emoji通常占4字节
// 查看字符串的字节长度
echo strlen($ascii_string) . "\n"; // 输出: 5
echo strlen($utf8_chinese) . "\n"; // 输出: 6 (2个中文字符×3字节)
echo strlen($emoji) . "\n"; // 输出: 4
// 查看字符数量(需要使用多字节函数)
echo mb_strlen($utf8_chinese, 'UTF-8') . "\n"; // 输出: 2
?>
字符串的创建方式
PHP提供了多种创建字符串的方式,每种方式都有其特点和适用场景。
1. 单引号字符串
单引号是最简单的字符串定义方式:
<?php
// 基本单引号字符串
$simple = 'Hello World';
$chinese = '你好,PHP编程';
$empty = '';
// 单引号中的特殊字符处理
$text = 'This is a \n test'; // \n 不会被解析为换行符
$quote = 'It\'s a test'; // 使用反斜杠转义单引号
$backslash = 'Path\\to\\file'; // 转义反斜杠
echo $simple . "\n";
echo $text . "\n"; // 输出: This is a \n test
echo $quote . "\n"; // 输出: It's a test
?>
单引号的特点:
- 变量和转义字符(除了\和')不会被解析
- 处理速度较快
- 适合包含纯文本内容
2. 双引号字符串
双引号字符串支持变量解析和转义字符:
<?php
$name = "张三";
$age = 25;
// 变量解析
$greeting = "你好,$name!"; // 输出: 你好,张三!
$info = "姓名:{$name},年龄:{$age}岁"; // 输出: 姓名:张三,年龄:25岁
// 转义字符解析
$multiline = "第一行\n第二行\t制表符";
$quote = "他说:\"PHP很有趣!\"";
$dollar = "价格:\$100";
echo $greeting . "\n";
echo $info . "\n";
echo $multiline . "\n";
echo $quote . "\n";
echo $dollar . "\n";
?>
双引号的特点:
- 支持变量解析(简单变量和数组元素)
- 支持转义字符(\n, \t, \r, \, $, "等)
- 处理速度比单引号稍慢
- 适合包含变量和需要转义的文本
3. Heredoc语法
Heredoc用于创建包含大量文本的字符串:
<?php
$name = "李四";
// Heredoc语法
$html = <<<HTML
<!DOCTYPE html>
<html>
<head>
<title>欢迎页面</title>
</head>
<body>
<h1>欢迎,{$name}!</h1>
<p>这是一个使用Heredoc创建的HTML模板。</p>
</body>
</html>
HTML;
echo $html;
// 不包含变量的Heredoc
$text = <<<TEXT
这是一个长文本,
可以跨越多行,
不需要担心引号问题。
"双引号"和'单引号'都可以直接使用。
TEXT;
echo $text;
?>
4. Nowdoc语法
Nowdoc类似于单引号,不解析变量和转义字符:
<?php
$name = "王五";
// Nowdoc语法
$template = <<<'TEMPLATE'
用户模板
---------
姓名:{$name} // 这里的变量不会被解析
邮箱:user@example.com
"引号"不会被转义
TEMPLATE;
echo $template;
?>
字符串的访问和操作
访问字符串中的字符
PHP提供了多种访问字符串中单个字符的方法:
<?php
$string = "Hello PHP";
// 方法1:使用花括号(PHP 7.4+已废弃)
$char1 = $string{0}; // 'H'
$char2 = $string{6}; // 'P'
// 方法2:使用方括号(推荐)
$char3 = $string[0]; // 'H'
$char4 = $string[6]; // 'P'
// 获取字符串长度并访问最后一个字符
$length = strlen($string);
$last_char = $string[$length - 1]; // 'P'
echo "第一个字符: {$char3}\n";
echo "第七个字符: {$char4}\n";
echo "最后一个字符: {$last_char}\n";
// 遍历字符串中的每个字符
for ($i = 0; $i < $length; $i++) {
echo "位置{$i}: {$string[$i]}\n";
}
?>
修改字符串中的字符
可以通过索引直接修改字符串中的字符:
<?php
$string = "Hello World";
// 修改单个字符
$string[0] = 'J'; // 变为 "Jello World"
$string[6] = 'P'; // 变为 "Jello Porld"
echo $string . "\n";
// 批量修改示例:首字母大写
function capitalizeFirst($str) {
if (empty($str)) return $str;
$str[0] = strtoupper($str[0]);
return $str;
}
echo capitalizeFirst("hello") . "\n"; // "Hello"
echo capitalizeFirst("world") . "\n"; // "World"
?>
字符串连接
基本连接操作
<?php
// 使用点号连接
$first_name = "张";
$last_name = "三";
$full_name = $first_name . $last_name;
echo $full_name . "\n"; // "张三"
// 连接多个字符串
$greeting = "你好," . $first_name . $last_name . "!";
echo $greeting . "\n"; // "你好,张三!"
// 连接不同类型的值
$age = 25;
$message = "我今年" . $age . "岁了";
echo $message . "\n"; // "我今年25岁了"
?>
使用连接赋值运算符
<?php
$text = "Hello";
$text .= " "; // 等同于 $text = $text . " ";
$text .= "World";
$text .= "!";
echo $text . "\n"; // "Hello World!"
// 构建HTML示例
$html = "<div>";
$html .= "<h1>标题</h1>";
$html .= "<p>段落内容</p>";
$html .= "</div>";
echo $html . "\n";
?>
复杂字符串构建
<?php
// 构建SQL查询示例
function buildSelectQuery($table, $columns = [], $where = '') {
$query = "SELECT ";
if (empty($columns)) {
$query .= "*";
} else {
$query .= implode(", ", $columns);
}
$query .= " FROM {$table}";
if (!empty($where)) {
$query .= " WHERE {$where}";
}
return $query;
}
echo buildSelectQuery("users", ["id", "name", "email"], "age > 18") . "\n";
// 输出: SELECT id, name, email FROM users WHERE age > 18
?>
字符串比较
相等性比较
<?php
// 使用 == 比较(值相等)
$str1 = "hello";
$str2 = "hello";
$str3 = "Hello"; // 大小写不同
var_dump($str1 == $str2); // bool(true)
var_dump($str1 == $str3); // bool(false)
// 使用 === 比较(值和类型都相等)
var_dump($str1 === $str2); // bool(true)
var_dump($str1 === "123"); // bool(false)
// 注意:PHP的字符串和数字比较
$number_str = "123";
$number = 123;
var_dump($number_str == $number); // bool(true) - 类型转换
var_dump($number_str === $number); // bool(false) - 类型不同
?>
大小写敏感比较
<?php
// strcmp() 函数 - 大小写敏感
$result1 = strcmp("Apple", "apple"); // 返回负数
$result2 = strcmp("apple", "Apple"); // 返回正数
$result3 = strcmp("hello", "hello"); // 返回0
echo "Apple vs apple: " . $result1 . "\n";
echo "apple vs Apple: " . $result2 . "\n";
echo "hello vs hello: " . $result3 . "\n";
// 比较结果说明
// 返回值 < 0: 第一个字符串小于第二个字符串
// 返回值 > 0: 第一个字符串大于第二个字符串
// 返回值 = 0: 两个字符串相等
?>
大小写不敏感比较
<?php
// strcasecmp() 函数 - 大小写不敏感
$result1 = strcasecmp("Apple", "apple"); // 返回0
$result2 = strcasecmp("Hello", "HELLO"); // 返回0
$result3 = strcasecmp("PHP", "Python"); // 返回非0
echo "strcasecmp结果:\n";
var_dump($result1); // int(0)
var_dump($result2); // int(0)
var_dump($result3); // int(-1)
// 实际应用:验证用户输入(不区分大小写)
$input = "ADMIN";
$correct_username = "admin";
if (strcasecmp($input, $correct_username) === 0) {
echo "用户名正确!\n";
} else {
echo "用户名错误!\n";
}
?>
自然排序比较
<?php
// strcmp vs strnatcmp
$files1 = ["file1.txt", "file10.txt", "file2.txt"];
$files2 = ["file1.txt", "file10.txt", "file2.txt"];
// 普通比较
sort($files1);
echo "普通排序: " . implode(", ", $files1) . "\n";
// 输出: file1.txt, file10.txt, file2.txt
// 自然排序
usort($files2, "strnatcmp");
echo "自然排序: " . implode(", ", $files2) . "\n";
// 输出: file1.txt, file2.txt, file10.txt
?>
特殊字符和转义序列
常用转义字符
<?php
// 在双引号字符串中的转义字符
$text = "换行符:\n制表符:\t回车符:\r";
echo $text . "\n";
// 引号转义
$single = "单引号:\'";
$double = "双引号:\"";
$backslash = "反斜杠:\\";
echo $single . "\n";
echo $double . "\n";
echo $backslash . "\n";
// 美元符号转义
$variable = "变量名使用:\$name 而不是变量的值";
echo $variable . "\n";
?>
八进制和十六进制字符
<?php
// 八进制表示
$octal_a = "\101"; // 'A' 的八进制表示
$octal_newline = "\012"; // 换行符的八进制表示
// 十六进制表示
$hex_a = "\x41"; // 'A' 的十六进制表示
$hex_newline = "\x0A"; // 换行符的十六进制表示
echo "八进制A: {$octal_a}\n";
echo "十六进制A: {$hex_a}\n";
?>
Unicode字符
<?php
// Unicode字符表示(PHP 7.0+)
$unicode1 = "\u{4F60}"; // '你'
$unicode2 = "\u{597D}"; // '好'
$emoji = "\u{1F603}"; // 😊
echo "Unicode字符: {$unicode1}{$unicode2}\n";
echo "Emoji: {$emoji}\n";
// 实际应用:生成特殊符号
$symbols = [
'check' => "\u{2713}", // ✓
'cross' => "\u{2717}", // ✗
'star' => "\u{2605}", // ★
];
echo "状态: " . $symbols['check'] . " 完成\n";
?>
中文字符串处理
UTF-8编码的重要性
处理中文字符串时,必须注意编码问题:
<?php
// 设置内部编码为UTF-8
mb_internal_encoding('UTF-8');
// 中文文本
$chinese_text = "你好,世界!这是一个中文字符串测试。";
// 错误的长度计算(按字节)
echo "字节长度: " . strlen($chinese_text) . "\n"; // 输出字节数
// 正确的字符数量计算
echo "字符数量: " . mb_strlen($chinese_text, 'UTF-8') . "\n";
// 截取子字符串
$substring = mb_substr($chinese_text, 0, 5, 'UTF-8');
echo "前5个字符: {$substring}\n";
// 获取单个字符
$first_char = mb_substr($chinese_text, 0, 1, 'UTF-8');
echo "第一个字符: {$first_char}\n";
?>
中文字符串操作最佳实践
<?php
class ChineseStringHelper {
/**
* 安全地获取中文字符串长度
*/
public static function length($str) {
return mb_strlen($str, 'UTF-8');
}
/**
* 安全地截取中文字符串
*/
public static function substring($str, $start, $length = null) {
return mb_substr($str, $start, $length, 'UTF-8');
}
/**
* 按字符分割中文字符串
*/
public static function split($str) {
$length = mb_strlen($str, 'UTF-8');
$chars = [];
for ($i = 0; $i < $length; $i++) {
$chars[] = mb_substr($str, $i, 1, 'UTF-8');
}
return $chars;
}
/**
* 检查是否包含中文字符
*/
public static function containsChinese($str) {
return preg_match('/[\x{4e00}-\x{9fa5}]/u', $str) > 0;
}
}
// 使用示例
$text = "PHP编程很有趣!";
echo "字符串长度: " . ChineseStringHelper::length($text) . "\n";
echo "前3个字符: " . ChineseStringHelper::substring($text, 0, 3) . "\n";
$chars = ChineseStringHelper::split($text);
echo "字符数组: " . implode(", ", $chars) . "\n";
echo "包含中文: " . (ChineseStringHelper::containsChinese($text) ? "是" : "否") . "\n";
?>
常见错误和解决方案
1. 编码问题
错误示例:
<?php
// 错误:没有指定编码
$chinese = "你好";
echo strlen($chinese); // 可能输出错误的结果
?>
正确做法:
<?php
// 正确:使用多字节函数并指定编码
$chinese = "你好";
echo mb_strlen($chinese, 'UTF-8');
?>
2. 字符串越界访问
错误示例:
<?php
$str = "hello";
echo $str[10]; // 访问不存在的字符,会产生Notice
?>
正确做法:
<?php
$str = "hello";
$length = strlen($str);
if ($length > 10) {
echo $str[10];
} else {
echo "索引超出范围";
}
?>
3. 混淆比较运算符
错误示例:
<?php
$str1 = "123";
$str2 = 123;
if ($str1 == $str2) { // 可能不是你想要的结果
echo "相等"; // 会输出,因为类型转换
}
?>
正确做法:
<?php
$str1 = "123";
$str2 = 123;
if ($str1 === $str2) { // 严格比较
echo "完全相等";
} else {
echo "类型或值不同";
}
?>
实际应用示例
用户输入处理
<?php
class InputProcessor {
/**
* 清理和验证用户名
*/
public static function processUsername($username) {
// 去除首尾空格
$username = trim($username);
// 检查长度
$length = mb_strlen($username, 'UTF-8');
if ($length < 3 || $length > 20) {
return ['success' => false, 'message' => '用户名长度必须在3-20个字符之间'];
}
// 检查是否包含特殊字符
if (preg_match('/[^a-zA-Z0-9\x{4e00}-\x{9fa5}_]/u', $username)) {
return ['success' => false, 'message' => '用户名只能包含字母、数字、中文和下划线'];
}
// 转换为安全格式
$safe_username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
return ['success' => true, 'username' => $safe_username];
}
}
// 测试
$result1 = InputProcessor::processUsername(" 张三123 ");
print_r($result1);
$result2 = InputProcessor::processUsername("ab");
print_r($result2);
$result3 = InputProcessor::processUsername("张三@#");
print_r($result3);
?>
简单的模板引擎
<?php
class SimpleTemplate {
private $template;
public function __construct($template) {
$this->template = $template;
}
/**
* 渲染模板
*/
public function render($variables = []) {
$result = $this->template;
foreach ($variables as $key => $value) {
// 简单的变量替换 {变量名}
$result = str_replace('{' . $key . '}', $value, $result);
}
return $result;
}
/**
* 渲染并输出
*/
public function display($variables = []) {
echo $this->render($variables);
}
}
// 使用示例
$template = new SimpleTemplate(<<<TEMPLATE
<div class="user-profile">
<h1>{name}</h1>
<p>邮箱:{email}</p>
<p>年龄:{age}岁</p>
<p>注册时间:{register_date}</p>
</div>
TEMPLATE);
$user_data = [
'name' => '张三',
'email' => 'zhangsan@example.com',
'age' => 25,
'register_date' => date('Y-m-d')
];
$template->display($user_data);
?>
本节练习
基础练习
- 使用四种不同的方式(单引号、双引号、Heredoc、Nowdoc)创建相同的字符串
- 编写一个函数,将字符串的首字母大写,其余字母小写
- 创建一个包含中文、英文、数字和特殊符号的字符串,并计算其字符数量
进阶练习
- 编写一个函数,检查字符串是否为回文(正读反读都一样)
- 实现一个简单的字符串加密函数(如凯撒密码)
- 创建一个函数,统计字符串中每个字符出现的次数
实战练习
- 完善InputProcessor类,添加邮箱和手机号验证功能
- 扩展SimpleTemplate类,支持条件渲染和循环
- 创建一个中文字符串处理工具类,包含常用方法
总结
本节我们学习了PHP字符串的基础知识,包括:
- 字符串的概念和内存表示
- 四种字符串创建方式及其特点
- 字符串的访问、修改和连接操作
- 字符串比较的各种方法
- 特殊字符和转义序列的使用
- 中文字符串处理的注意事项
- 常见错误和解决方案
掌握这些基础知识是学习高级字符串处理技巧的前提。在下一节中,我们将学习PHP提供的强大字符串处理函数,这些函数将大大提高我们处理文本的能力。
💡 学习建议:多动手练习每个示例,特别注意中文字符串处理中的编码问题。在实际开发中,建议总是使用UTF-8编码和多字节字符串函数来处理中文内容。