PHP fgetc() 函数
说明: fgetc() 函数用于从文件指针中读取单个字符。
语法
string|false fgetc ( resource $handle )
参数说明
| 参数 |
描述 |
必需 |
| handle |
由 fopen() 打开的文件指针 |
是 |
返回值
- 成功时返回包含一个字符的字符串
- 到达文件末尾(EOF)时返回
FALSE
- 读取失败时返回
FALSE
注意事项
- fgetc() 每次只读取一个字节(一个字符),适用于单字节编码(如ASCII)
- 对于多字节字符(如UTF-8),fgetc() 可能只读取字符的一部分,导致乱码
- 文件指针必须是有效的,并且指向一个成功打开的文件
- 读取后文件指针会自动移动到下一个字符
- 如果文件为空或已到达文件末尾,fgetc() 会返回 false
- 使用二进制模式("rb")打开文件可以避免换行符转换问题
字符编码说明
不同编码下的字符长度:
| 编码 |
英文字符 |
中文字符 |
表情符号 |
| ASCII |
1字节 |
不支持 |
不支持 |
| ISO-8859-1 |
1字节 |
不支持 |
不支持 |
| UTF-8 |
1字节 |
3字节 |
4字节 |
| UTF-16 |
2字节 |
2字节 |
4字节 |
注意: 由于 fgetc() 每次读取一个字节,在处理多字节编码时需要使用其他方法,如 mb_substr() 或适当的流处理。
示例
示例 1:基本使用 - 逐字符读取文件
<?php
// 创建测试文件
$filename = "test.txt";
file_put_contents($filename, "Hello World!");
// 打开文件
$handle = fopen($filename, "r");
if (!$handle) {
die("无法打开文件");
}
echo "逐字符读取文件内容:\n";
// 逐字符读取
while (($char = fgetc($handle)) !== false) {
echo "字符: '" . $char . "' ASCII: " . ord($char) . "\n";
}
// 关闭文件
fclose($handle);
// 清理测试文件
unlink($filename);
?>
示例 2:统计字符频率
<?php
/**
* 统计文件中每个字符的出现频率
*/
function count_char_frequency($filename) {
$handle = fopen($filename, "r");
if (!$handle) {
return false;
}
$frequency = [];
$total_chars = 0;
while (($char = fgetc($handle)) !== false) {
// 跳过换行符和空格(可选)
if ($char === "\n" || $char === "\r" || $char === " ") {
continue;
}
if (!isset($frequency[$char])) {
$frequency[$char] = 0;
}
$frequency[$char]++;
$total_chars++;
}
fclose($handle);
// 按频率降序排序
arsort($frequency);
return [
'frequency' => $frequency,
'total_chars' => $total_chars,
'unique_chars' => count($frequency)
];
}
// 创建测试文件
$test_file = "char_test.txt";
$content = "This is a test file for character frequency analysis.\n";
$content .= "It contains various characters: a, b, c, 1, 2, 3, !, @, #, etc.\n";
file_put_contents($test_file, $content);
// 统计字符频率
$result = count_char_frequency($test_file);
if ($result) {
echo "字符频率分析结果:\n";
echo "====================\n";
echo "总字符数(排除空格和换行): " . $result['total_chars'] . "\n";
echo "唯一字符数: " . $result['unique_chars'] . "\n\n";
echo "字符频率(前10个):\n";
$count = 0;
foreach ($result['frequency'] as $char => $freq) {
if (++$count > 10) break;
$char_display = ($char === "\t") ? "\\t" :
($char === "\n") ? "\\n" :
($char === "\r") ? "\\r" : $char;
$percentage = ($freq / $result['total_chars']) * 100;
echo sprintf("字符 '%s' (ASCII: %3d) : %4d 次 (%5.1f%%)\n",
$char_display, ord($char), $freq, $percentage);
}
}
// 清理
unlink($test_file);
?>
示例 3:UTF-8多字节字符处理
<?php
/**
* 安全的多字节字符读取类
* 正确处理UTF-8等多字节编码
*/
class MultibyteCharReader {
private $handle;
private $encoding;
public function __construct($filename, $encoding = 'UTF-8') {
$this->encoding = $encoding;
$this->handle = fopen($filename, "rb"); // 二进制模式
if (!$this->handle) {
throw new Exception("无法打开文件: " . $filename);
}
}
/**
* 读取一个完整的多字节字符
*/
public function readChar() {
$first_byte = fgetc($this->handle);
if ($first_byte === false) {
return false;
}
// 判断UTF-8字符的字节数
$bytes = 1;
$ord = ord($first_byte);
if ($ord < 0x80) {
// ASCII字符,1字节
return $first_byte;
} elseif (($ord & 0xE0) == 0xC0) {
// 2字节字符
$bytes = 2;
} elseif (($ord & 0xF0) == 0xE0) {
// 3字节字符
$bytes = 3;
} elseif (($ord & 0xF8) == 0xF0) {
// 4字节字符
$bytes = 4;
} else {
// 无效的UTF-8起始字节
return $first_byte;
}
// 读取剩余字节
$char = $first_byte;
for ($i = 1; $i < $bytes; $i++) {
$next_byte = fgetc($this->handle);
if ($next_byte === false) {
break;
}
$char .= $next_byte;
}
return $char;
}
/**
* 读取指定数量的字符
*/
public function readChars($count) {
$result = '';
for ($i = 0; $i < $count; $i++) {
$char = $this->readChar();
if ($char === false) {
break;
}
$result .= $char;
}
return $result;
}
/**
* 关闭文件
*/
public function close() {
if ($this->handle) {
fclose($this->handle);
$this->handle = null;
}
}
public function __destruct() {
$this->close();
}
/**
* 获取当前文件位置
*/
public function getPosition() {
if ($this->handle) {
return ftell($this->handle);
}
return false;
}
}
// 使用示例
echo "多字节字符读取演示:\n";
echo "===================\n";
// 创建包含多字节字符的测试文件
$utf8_file = "utf8_test.txt";
$utf8_content = "Hello 你好 🌍 World! 测试 🎉 表情符号。\n";
$utf8_content .= "ASCII: ABC abc 123\n";
$utf8_content .= "中文:这是一段测试文本。\n";
$utf8_content .= "混合:Hello 世界!🚀 火箭\n";
file_put_contents($utf8_file, $utf8_content, LOCK_EX);
echo "文件内容预览:\n";
echo $utf8_content . "\n";
echo "逐字符分析:\n";
try {
$reader = new MultibyteCharReader($utf8_file, 'UTF-8');
$char_count = 0;
while (($char = $reader->readChar()) !== false) {
$char_count++;
// 跳过换行符以便更好显示
if ($char === "\n") {
echo "[换行符]\n";
continue;
}
// 显示字符信息
$bytes = strlen($char);
$hex = '';
for ($i = 0; $i < $bytes; $i++) {
$hex .= sprintf('%02X ', ord($char[$i]));
}
echo sprintf("字符 #%03d: '%s' (字节: %d, 十六进制: %s)\n",
$char_count, $char, $bytes, trim($hex));
// 只显示前30个字符的详细分析
if ($char_count >= 30) {
echo "... 只显示前30个字符\n";
break;
}
}
$reader->close();
echo "\n字符总数(包括换行符): " . $char_count . "\n";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 清理
unlink($utf8_file);
?>
示例 4:解析简单文件格式
<?php
/**
* 简单CSV解析器(使用fgetc逐字符解析)
* 支持引号和转义字符
*/
class SimpleCsvParser {
private $handle;
private $delimiter;
private $enclosure;
private $escape;
public function __construct($filename, $delimiter = ',', $enclosure = '"', $escape = '\\') {
$this->delimiter = $delimiter;
$this->enclosure = $enclosure;
$this->escape = $escape;
$this->handle = fopen($filename, "r");
if (!$this->handle) {
throw new Exception("无法打开CSV文件: " . $filename);
}
}
/**
* 读取一行CSV数据
*/
public function readRow() {
if (feof($this->handle)) {
return false;
}
$row = [];
$field = '';
$in_quotes = false;
$prev_char = '';
while (($char = fgetc($this->handle)) !== false) {
// 处理转义字符
if ($prev_char === $this->escape) {
$field .= $char;
$prev_char = '';
continue;
}
// 处理引号
if ($char === $this->enclosure) {
if ($in_quotes) {
// 查看下一个字符是否是另一个引号(转义引号)
$next_char = fgetc($this->handle);
if ($next_char === $this->enclosure) {
$field .= $char;
continue;
} else {
// 将指针移回
fseek($this->handle, -1, SEEK_CUR);
$in_quotes = false;
}
} else {
$in_quotes = true;
}
$prev_char = $char;
continue;
}
// 处理分隔符
if ($char === $this->delimiter && !$in_quotes) {
$row[] = $field;
$field = '';
$prev_char = $char;
continue;
}
// 处理行结束
if ($char === "\n" && !$in_quotes) {
$row[] = $field;
return $row;
}
if ($char === "\r") {
// 跳过回车符,等待换行符
continue;
}
// 添加字符到字段
$field .= $char;
$prev_char = $char;
}
// 文件结束,返回最后一个字段
if ($field !== '' || !empty($row)) {
$row[] = $field;
return $row;
}
return false;
}
/**
* 读取所有数据
*/
public function readAll() {
$data = [];
while (($row = $this->readRow()) !== false) {
$data[] = $row;
}
return $data;
}
/**
* 关闭文件
*/
public function close() {
if ($this->handle) {
fclose($this->handle);
}
}
public function __destruct() {
$this->close();
}
}
// 使用示例
echo "简单CSV解析器演示:\n";
echo "==================\n";
// 创建测试CSV文件
$csv_content = 'Name,Age,City,Description
John Doe,25,"New York, USA","Software Engineer"
Jane Smith,30,"Los Angeles","Data Analyst, \"Expert\""
Bob Johnson,35,"Chicago","Manager
Handles multiple teams"
Alice Brown,28,"Miami","Marketing Specialist"';
$csv_file = "test.csv";
file_put_contents($csv_file, $csv_content);
try {
$parser = new SimpleCsvParser($csv_file);
echo "解析CSV文件内容:\n";
echo "原始内容:\n";
echo $csv_content . "\n\n";
echo "解析结果:\n";
$data = $parser->readAll();
foreach ($data as $row_num => $row) {
echo "行 " . ($row_num + 1) . ": ";
foreach ($row as $col_num => $cell) {
echo "[$col_num: \"" . $cell . "\"] ";
}
echo "\n";
}
$parser->close();
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 清理
unlink($csv_file);
?>
示例 5:文本分析工具
<?php
/**
* 文本分析工具类
* 使用fgetc()进行详细的文本分析
*/
class TextAnalyzer {
private $handle;
private $filename;
public function __construct($filename) {
$this->filename = $filename;
$this->handle = fopen($filename, "r");
if (!$this->handle) {
throw new Exception("无法打开文件: " . $filename);
}
}
/**
* 统计基本文本信息
*/
public function analyzeBasic() {
rewind($this->handle);
$stats = [
'chars' => 0,
'letters' => 0,
'digits' => 0,
'spaces' => 0,
'punctuation' => 0,
'lines' => 1, // 从1开始计数
'words' => 0,
'in_word' => false
];
$prev_char = '';
while (($char = fgetc($this->handle)) !== false) {
$stats['chars']++;
// 统计字母
if (ctype_alpha($char)) {
$stats['letters']++;
if (!$stats['in_word']) {
$stats['words']++;
$stats['in_word'] = true;
}
}
// 统计数字
elseif (ctype_digit($char)) {
$stats['digits']++;
if (!$stats['in_word']) {
$stats['words']++;
$stats['in_word'] = true;
}
}
// 统计空格
elseif (ctype_space($char)) {
$stats['spaces']++;
$stats['in_word'] = false;
// 统计行数
if ($char === "\n") {
$stats['lines']++;
}
}
// 统计标点符号
elseif (ctype_punct($char)) {
$stats['punctuation']++;
$stats['in_word'] = false;
} else {
$stats['in_word'] = false;
}
$prev_char = $char;
}
rewind($this->handle);
return $stats;
}
/**
* 查找最长的单词
*/
public function findLongestWord() {
rewind($this->handle);
$longest_word = '';
$current_word = '';
$in_word = false;
while (($char = fgetc($this->handle)) !== false) {
if (ctype_alnum($char)) {
$current_word .= $char;
$in_word = true;
} else {
if ($in_word) {
if (strlen($current_word) > strlen($longest_word)) {
$longest_word = $current_word;
}
$current_word = '';
$in_word = false;
}
}
}
// 检查最后一个单词
if ($in_word && strlen($current_word) > strlen($longest_word)) {
$longest_word = $current_word;
}
rewind($this->handle);
return $longest_word;
}
/**
* 搜索字符序列
*/
public function searchSequence($sequence) {
rewind($this->handle);
$seq_length = strlen($sequence);
$buffer = '';
$positions = [];
$position = 0;
while (($char = fgetc($this->handle)) !== false) {
$buffer .= $char;
// 保持缓冲区长度与序列长度相同
if (strlen($buffer) > $seq_length) {
$buffer = substr($buffer, 1);
$position++;
}
// 检查是否匹配
if ($buffer === $sequence) {
$positions[] = $position;
}
}
rewind($this->handle);
return $positions;
}
/**
* 关闭文件
*/
public function close() {
if ($this->handle) {
fclose($this->handle);
$this->handle = null;
}
}
public function __destruct() {
$this->close();
}
}
// 使用示例
echo "文本分析工具演示:\n";
echo "==================\n";
// 创建测试文本文件
$text_file = "analysis_test.txt";
$text_content = "The quick brown fox jumps over the lazy dog.\n";
$text_content .= "This sentence contains 35 letters and 8 words.\n";
$text_content .= "Numbers: 123, 456, 789. Punctuation: !, ?, ,, ., ;, :\n";
$text_content .= "Special characters: @#$%^&*()_+-=[]{}|;:',./<>?`~\n";
$text_content .= "The longestwordinthistextis: supercalifragilisticexpialidocious\n";
file_put_contents($text_file, $text_content);
try {
$analyzer = new TextAnalyzer($text_file);
// 基本分析
echo "基本文本分析:\n";
$stats = $analyzer->analyzeBasic();
echo "总字符数: " . $stats['chars'] . "\n";
echo "字母数: " . $stats['letters'] . "\n";
echo "数字数: " . $stats['digits'] . "\n";
echo "空格数: " . $stats['spaces'] . "\n";
echo "标点符号数: " . $stats['punctuation'] . "\n";
echo "行数: " . $stats['lines'] . "\n";
echo "单词数: " . $stats['words'] . "\n";
// 查找最长单词
echo "\n查找最长单词:\n";
$longest = $analyzer->findLongestWord();
echo "最长单词: \"" . $longest . "\" (长度: " . strlen($longest) . ")\n";
// 搜索序列
echo "\n搜索字符序列:\n";
$search_seq = "the";
$positions = $analyzer->searchSequence($search_seq);
echo "序列 \"" . $search_seq . "\" 出现位置: ";
if (empty($positions)) {
echo "未找到";
} else {
echo implode(", ", $positions) . " (共 " . count($positions) . " 次)";
}
$analyzer->close();
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 清理
unlink($text_file);
?>
示例 6:简单加密/解密工具
<?php
/**
* 简单的字符级加密/解密工具
* 使用fgetc()逐字符处理
*/
class SimpleCipher {
private $key;
public function __construct($key = 'secret') {
$this->key = $key;
}
/**
* 加密文件
*/
public function encryptFile($source, $dest) {
$source_handle = fopen($source, "r");
$dest_handle = fopen($dest, "w");
if (!$source_handle || !$dest_handle) {
return false;
}
$key_length = strlen($this->key);
$key_index = 0;
while (($char = fgetc($source_handle)) !== false) {
// 简单的XOR加密
$encrypted_char = chr(ord($char) ^ ord($this->key[$key_index]));
fwrite($dest_handle, $encrypted_char);
$key_index = ($key_index + 1) % $key_length;
}
fclose($source_handle);
fclose($dest_handle);
return true;
}
/**
* 解密文件(XOR加密是对称的)
*/
public function decryptFile($source, $dest) {
// XOR加密中,加密和解密是相同的操作
return $this->encryptFile($source, $dest);
}
/**
* Caesar密码加密
*/
public function caesarEncryptFile($source, $dest, $shift = 3) {
$source_handle = fopen($source, "r");
$dest_handle = fopen($dest, "w");
if (!$source_handle || !$dest_handle) {
return false;
}
while (($char = fgetc($source_handle)) !== false) {
// 只加密字母
if (ctype_alpha($char)) {
$base = ctype_upper($char) ? ord('A') : ord('a');
$encrypted_char = chr((ord($char) - $base + $shift) % 26 + $base);
fwrite($dest_handle, $encrypted_char);
} else {
fwrite($dest_handle, $char);
}
}
fclose($source_handle);
fclose($dest_handle);
return true;
}
/**
* Caesar密码解密
*/
public function caesarDecryptFile($source, $dest, $shift = 3) {
return $this->caesarEncryptFile($source, $dest, 26 - $shift);
}
}
// 使用示例
echo "简单加密/解密工具演示:\n";
echo "======================\n";
// 创建测试文件
$plain_file = "plain.txt";
$encrypted_file = "encrypted.txt";
$decrypted_file = "decrypted.txt";
$plain_text = "Hello, this is a secret message!\n";
$plain_text .= "It contains sensitive information.\n";
$plain_text .= "123 numbers and special characters: !@#$%^&*()\n";
file_put_contents($plain_file, $plain_text);
echo "原始文本:\n";
echo $plain_text . "\n";
$cipher = new SimpleCipher("mysecretkey");
// XOR加密演示
echo "XOR加密演示:\n";
if ($cipher->encryptFile($plain_file, $encrypted_file)) {
echo "✅ 加密成功\n";
echo "加密后的内容(十六进制):\n";
$encrypted_content = file_get_contents($encrypted_file);
for ($i = 0; $i < min(50, strlen($encrypted_content)); $i++) {
echo sprintf('%02X ', ord($encrypted_content[$i]));
}
echo "...\n\n";
// 解密
if ($cipher->decryptFile($encrypted_file, $decrypted_file)) {
echo "✅ 解密成功\n";
echo "解密后的内容:\n";
echo file_get_contents($decrypted_file) . "\n";
}
}
echo "\nCaesar密码演示(移位3):\n";
$caesar_encrypted = "caesar_encrypted.txt";
$caesar_decrypted = "caesar_decrypted.txt";
if ($cipher->caesarEncryptFile($plain_file, $caesar_encrypted, 3)) {
echo "✅ Caesar加密成功\n";
echo "加密后的内容:\n";
echo file_get_contents($caesar_encrypted) . "\n";
if ($cipher->caesarDecryptFile($caesar_encrypted, $caesar_decrypted, 3)) {
echo "✅ Caesar解密成功\n";
echo "解密后的内容:\n";
echo file_get_contents($caesar_decrypted) . "\n";
}
}
// 清理测试文件
$files = [$plain_file, $encrypted_file, $decrypted_file, $caesar_encrypted, $caesar_decrypted];
foreach ($files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
?>
fgetc() 与其他读取函数的对比
| 函数 |
读取单位 |
返回值 |
适用场景 |
性能 |
fgetc() |
单个字符 |
字符串(1字符)或 false |
字符级处理、文本分析、解析 |
较慢(逐字符I/O) |
fgets() |
一行 |
字符串(一行)或 false |
行级处理、日志分析、配置读取 |
较快 |
fread() |
指定字节数 |
字符串或 false |
二进制文件、大文件分块读取 |
快(可控制缓冲区大小) |
file_get_contents() |
整个文件 |
字符串或 false |
小文件读取、配置文件 |
最快(一次性读取) |
fscanf() |
格式化数据 |
混合类型 |
格式化文本解析 |
中等 |
相关函数
- 选择正确的读取函数:根据需求选择fgetc()、fgets()或fread()
- 处理多字节字符:对于UTF-8等编码,使用专门的函数或自定义逻辑
- 检查文件指针:使用fgetc()前确保文件指针有效
- 正确检测EOF:使用
while (($char = fgetc($handle)) !== false)模式
- 考虑性能:fgetc()逐字符读取较慢,适用于小文件或需要字符级处理的场景
- 使用二进制模式:对于二进制文件或需要精确控制的情况,使用"rb"模式
- 错误处理:检查fgetc()的返回值,区分EOF和读取错误
- 资源管理:使用后关闭文件指针,避免资源泄漏
模式1:基本逐字符读取
$handle = fopen($file, "r");
while (($char = fgetc($handle)) !== false) {
// 处理每个字符
}
fclose($handle);
模式2:字符统计
$handle = fopen($file, "r");
$char_count = 0;
while (($char = fgetc($handle)) !== false) {
if ($char === 'a') { // 统计特定字符
$char_count++;
}
}
fclose($handle);
模式3:简单解析器
$handle = fopen($file, "r");
$in_quotes = false;
$current_field = '';
while (($char = fgetc($handle)) !== false) {
if ($char === '"') {
$in_quotes = !$in_quotes;
} elseif ($char === ',' && !$in_quotes) {
// 字段结束
processField($current_field);
$current_field = '';
} else {
$current_field .= $char;
}
}
fclose($handle);