bool ftruncate ( resource $handle , int $size )
| 参数 | 描述 |
|---|---|
handle |
文件指针资源,通常由fopen()函数创建,且必须以可写模式打开(如"r+"、"w"、"a+"等) |
size |
截断后文件的大小(字节数)。如果大于原文件大小,则用空字节填充到指定大小;如果小于原文件大小,则截断多余部分。 |
truefalse<?php
// 创建测试文件
$filename = 'test.txt';
$content = "This is a test file with some content that we will truncate.";
file_put_contents($filename, $content);
echo "原始文件大小: " . filesize($filename) . " 字节<br>";
echo "原始内容: " . htmlspecialchars(file_get_contents($filename)) . "<br><br>";
// 以读写模式打开文件
$handle = fopen($filename, 'r+');
if ($handle) {
// 截断到20字节
if (ftruncate($handle, 20)) {
echo "截断到20字节成功<br>";
// 注意:ftruncate后文件指针位置不变,需要重新定位到文件开头读取
fseek($handle, 0);
echo "截断后内容: " . htmlspecialchars(fread($handle, filesize($filename))) . "<br>";
echo "截断后大小: " . filesize($filename) . " 字节<br><br>";
} else {
echo "截断失败<br>";
}
// 再截断到50字节(扩展文件)
if (ftruncate($handle, 50)) {
echo "扩展到50字节成功<br>";
fseek($handle, 0);
echo "扩展后内容: " . htmlspecialchars(fread($handle, filesize($filename))) . "<br>";
echo "扩展后大小: " . filesize($filename) . " 字节<br>";
// 注意:扩展的部分是空字节,显示为空白
}
fclose($handle);
}
// 清理
unlink($filename);
?>
<?php
// 模拟日志文件轮转
class LogRotator {
private $filename;
private $maxSize; // 最大文件大小(字节)
private $backupCount; // 保留的备份文件数量
public function __construct($filename, $maxSize = 1024 * 1024, $backupCount = 5) {
$this->filename = $filename;
$this->maxSize = $maxSize;
$this->backupCount = $backupCount;
}
/**
* 检查并轮转日志文件
*/
public function rotateIfNeeded() {
if (!file_exists($this->filename)) {
return false;
}
$currentSize = filesize($this->filename);
if ($currentSize < $this->maxSize) {
return false; // 文件未达到最大大小,无需轮转
}
echo "日志文件达到 {$this->maxSize} 字节,开始轮转...<br>";
// 1. 重命名现有的备份文件
for ($i = $this->backupCount - 1; $i >= 0; $i--) {
$oldFile = $this->getBackupFilename($i);
$newFile = $this->getBackupFilename($i + 1);
if (file_exists($oldFile)) {
rename($oldFile, $newFile);
}
}
// 2. 将当前日志文件重命名为第一个备份
rename($this->filename, $this->getBackupFilename(0));
// 3. 创建新的日志文件
touch($this->filename);
echo "日志轮转完成<br>";
return true;
}
/**
* 截断日志文件(保留最后N字节)
*/
public function truncateLog($keepBytes = 1024) {
if (!file_exists($this->filename)) {
return false;
}
$fileSize = filesize($this->filename);
if ($fileSize <= $keepBytes) {
return false; // 文件小于等于要保留的大小,无需截断
}
// 打开文件
$handle = fopen($this->filename, 'r+');
if (!$handle) {
return false;
}
// 移动到要保留的内容的开始位置
$offset = $fileSize - $keepBytes;
fseek($handle, $offset);
// 读取要保留的内容
$keepContent = fread($handle, $keepBytes);
// 截断文件并写入保留的内容
rewind($handle);
ftruncate($handle, $keepBytes);
fwrite($handle, $keepContent);
fclose($handle);
echo "日志文件已截断,保留了最后 {$keepBytes} 字节<br>";
return true;
}
/**
* 获取备份文件名
*/
private function getBackupFilename($index) {
if ($index === 0) {
return $this->filename . '.0';
}
return $this->filename . '.' . $index;
}
}
// 使用示例
// 创建一个大日志文件
$logFile = 'app.log';
$logContent = str_repeat("Log entry: " . date('Y-m-d H:i:s') . " - Some log message\n", 1000);
file_put_contents($logFile, $logContent);
echo "原始日志文件大小: " . filesize($logFile) . " 字节<br>";
$rotator = new LogRotator($logFile, 10240, 3); // 最大10KB,保留3个备份
// 尝试轮转
$rotator->rotateIfNeeded();
// 如果文件仍然很大,截断保留最后5KB
$rotator->truncateLog(5120);
echo "处理后文件大小: " . filesize($logFile) . " 字节<br>";
// 清理
unlink($logFile);
for ($i = 0; $i <= 3; $i++) {
$backupFile = $logFile . '.' . $i;
if (file_exists($backupFile)) {
unlink($backupFile);
}
}
?>
<?php
// 模拟数据库文件清理
class DatabaseFileManager {
private $filename;
public function __construct($filename) {
$this->filename = $filename;
}
/**
* 清理数据库文件中的过期数据(模拟)
*/
public function cleanupOldData($maxRecords = 100) {
if (!file_exists($this->filename)) {
return false;
}
// 打开文件
$handle = fopen($this->filename, 'r+');
if (!$handle) {
return false;
}
// 读取所有记录
$records = [];
while (!feof($handle)) {
$line = fgets($handle);
if (trim($line) !== '') {
$records[] = $line;
}
}
// 如果记录数超过最大限制,删除最旧的记录
if (count($records) > $maxRecords) {
$recordsToKeep = array_slice($records, -$maxRecords);
// 截断文件并写入保留的记录
rewind($handle);
ftruncate($handle, 0);
foreach ($recordsToKeep as $record) {
fwrite($handle, $record);
}
$removedCount = count($records) - $maxRecords;
echo "已删除 {$removedCount} 条旧记录,保留 {$maxRecords} 条最新记录<br>";
} else {
echo "记录数未超过限制,无需清理<br>";
}
fclose($handle);
return true;
}
/**
* 压缩数据库文件(删除空行和空白)
*/
public function compressFile() {
if (!file_exists($this->filename)) {
return false;
}
// 读取文件内容
$content = file_get_contents($this->filename);
$originalSize = strlen($content);
// 移除空行和多余空白
$lines = explode("\n", $content);
$compressedLines = [];
foreach ($lines as $line) {
$trimmedLine = trim($line);
if ($trimmedLine !== '') {
$compressedLines[] = $trimmedLine;
}
}
$compressedContent = implode("\n", $compressedLines);
$compressedSize = strlen($compressedContent);
// 如果压缩后有变化,则写入文件
if ($compressedSize < $originalSize) {
$handle = fopen($this->filename, 'r+');
if ($handle) {
ftruncate($handle, 0);
fwrite($handle, $compressedContent);
fclose($handle);
$saved = $originalSize - $compressedSize;
echo "文件压缩完成,节省 {$saved} 字节<br>";
return true;
}
} else {
echo "文件无需压缩<br>";
}
return false;
}
}
// 使用示例
// 创建模拟数据库文件
$dbFile = 'data.db';
$data = "";
for ($i = 1; $i <= 150; $i++) {
$data .= "Record $i: " . str_repeat('x', 50) . "\n";
}
file_put_contents($dbFile, $data);
echo "原始数据库文件大小: " . filesize($dbFile) . " 字节<br>";
$manager = new DatabaseFileManager($dbFile);
// 清理旧数据
$manager->cleanupOldData(100);
// 压缩文件
$manager->compressFile();
echo "处理后文件大小: " . filesize($dbFile) . " 字节<br>";
// 清理
unlink($dbFile);
?>
fseek()重新定位flock()进行文件锁定,避免数据损坏| 特性 | ftruncate() | file_put_contents() |
|---|---|---|
| 主要用途 | 调整文件大小 | 写入文件内容 |
| 文件处理 | 需要已打开的文件句柄 | 直接操作文件路径 |
| 性能 | 对于大文件调整大小更高效 | 适合写入内容或创建新文件 |
| 灵活性 | 可以精确控制文件大小 | 更关注文件内容而非大小 |
| 使用场景 | 日志轮转、文件清理、空间预分配 | 配置文件写入、数据导出、内容更新 |
fopen() - 打开文件或URLfclose() - 关闭一个已打开的文件指针fseek() - 在文件指针中定位ftell() - 返回文件指针读/写的位置filesize() - 获取文件大小file_put_contents() - 将字符串写入文件unlink() - 删除文件flock() - 便携式文件锁定轮转日志文件,限制日志文件大小,清理旧日志数据。
清理数据库文件中的过期记录,压缩数据库文件。
定期清理临时文件,释放磁盘空间。
预先分配文件空间,提高后续写入性能。