こんにちは!今回は、PHPの標準関数であるsprintf()について詳しく解説していきます。文字列を指定したフォーマットで整形できる、非常に便利で強力な関数です!
sprintf関数とは?
sprintf()関数は、指定したフォーマットに従って文字列を整形する関数です。
C言語のsprintf関数と同様の機能を持ち、数値のゼロ埋め、小数点以下の桁数指定、文字列の幅揃えなど、様々なフォーマット指定が可能です!
基本的な構文
sprintf(string $format, mixed ...$values): string
- $format: フォーマット文字列(変換指定子を含む)
- $values: フォーマットに埋め込む値(可変長引数)
- 戻り値: フォーマットされた文字列
フォーマット指定子の基本
フォーマット指定子は % で始まり、以下の形式を取ります:
%[引数番号$][フラグ][幅][.精度]型指定子
主な型指定子
| 型指定子 | 説明 | 例 |
|---|---|---|
%s | 文字列 | sprintf(“%s”, “hello”) → “hello” |
%d | 整数(10進数) | sprintf(“%d”, 123) → “123” |
%f | 浮動小数点数 | sprintf(“%f”, 3.14) → “3.140000” |
%b | 2進数 | sprintf(“%b”, 5) → “101” |
%o | 8進数 | sprintf(“%o”, 8) → “10” |
%x | 16進数(小文字) | sprintf(“%x”, 255) → “ff” |
%X | 16進数(大文字) | sprintf(“%X”, 255) → “FF” |
%c | ASCII文字 | sprintf(“%c”, 65) → “A” |
%e | 科学表記(小文字) | sprintf(“%e”, 1000) → “1.000000e+3” |
%E | 科学表記(大文字) | sprintf(“%E”, 1000) → “1.000000E+3” |
%% | % 文字そのもの | sprintf(“%%”) → “%” |
基本的な使用例
文字列の挿入
$name = "田中太郎";
$age = 30;
$message = sprintf("私の名前は%sで、%d歳です。", $name, $age);
echo $message . "\n";
// 出力: 私の名前は田中太郎で、30歳です。
数値のフォーマット
// 整数
echo sprintf("%d", 123) . "\n"; // 123
echo sprintf("%d", 123.45) . "\n"; // 123 (整数に変換)
// 浮動小数点数
echo sprintf("%f", 3.14159) . "\n"; // 3.141590
echo sprintf("%.2f", 3.14159) . "\n"; // 3.14 (小数点以下2桁)
echo sprintf("%.0f", 3.14159) . "\n"; // 3 (小数点以下なし)
ゼロ埋め
// 数値のゼロ埋め
echo sprintf("%03d", 5) . "\n"; // 005
echo sprintf("%05d", 123) . "\n"; // 00123
echo sprintf("%08d", 42) . "\n"; // 00000042
// IDの生成
$userId = 42;
$formattedId = sprintf("USER%06d", $userId);
echo $formattedId . "\n"; // USER000042
幅指定
// 右詰め(デフォルト)
echo sprintf("%10s", "hello") . "\n"; // " hello"
echo sprintf("%10d", 123) . "\n"; // " 123"
// 左詰め(-フラグ)
echo sprintf("%-10s", "hello") . "\n"; // "hello "
echo sprintf("%-10d", 123) . "\n"; // "123 "
実践的な使用例
例1: 金額のフォーマット
class MoneyFormatter {
public static function format($amount, $currency = '円') {
// 3桁ごとにカンマを入れる
$formatted = number_format($amount);
return sprintf("%s%s", $formatted, $currency);
}
public static function formatDetailed($amount, $currency = '円') {
// 小数点以下2桁まで表示
return sprintf("¥%s (%s)",
number_format($amount, 2),
$currency
);
}
public static function formatTable($items) {
echo "商品名 価格\n";
echo str_repeat("-", 30) . "\n";
foreach ($items as $item) {
// 商品名は20文字、価格は右詰め10文字
echo sprintf("%-20s %10s\n",
$item['name'],
self::format($item['price'])
);
}
}
}
// 使用例
echo MoneyFormatter::format(1234567) . "\n";
// 出力: 1,234,567円
echo MoneyFormatter::formatDetailed(1234.56) . "\n";
// 出力: ¥1,234.56 (円)
$items = [
['name' => 'ノートパソコン', 'price' => 150000],
['name' => 'マウス', 'price' => 2500],
['name' => 'キーボード', 'price' => 8000],
];
MoneyFormatter::formatTable($items);
/*
出力:
商品名 価格
------------------------------
ノートパソコン 150,000円
マウス 2,500円
キーボード 8,000円
*/
例2: 日時のフォーマット
class DateTimeFormatter {
public static function formatTimestamp($timestamp) {
$date = date('Y-m-d', $timestamp);
$time = date('H:i:s', $timestamp);
return sprintf("%s %s", $date, $time);
}
public static function formatDuration($seconds) {
$hours = floor($seconds / 3600);
$minutes = floor(($seconds % 3600) / 60);
$secs = $seconds % 60;
return sprintf("%02d:%02d:%02d", $hours, $minutes, $secs);
}
public static function formatFileDate($timestamp) {
// ファイル名に使える日時形式
return sprintf("%s_%s",
date('Ymd', $timestamp),
date('His', $timestamp)
);
}
public static function formatCountdown($targetTimestamp) {
$now = time();
$diff = $targetTimestamp - $now;
if ($diff <= 0) {
return "終了しました";
}
$days = floor($diff / 86400);
$hours = floor(($diff % 86400) / 3600);
$minutes = floor(($diff % 3600) / 60);
return sprintf("残り %d日 %02d時間 %02d分", $days, $hours, $minutes);
}
}
// 使用例
$timestamp = time();
echo DateTimeFormatter::formatTimestamp($timestamp) . "\n";
// 出力: 2024-02-13 15:30:45
echo DateTimeFormatter::formatDuration(3665) . "\n";
// 出力: 01:01:05
echo DateTimeFormatter::formatFileDate($timestamp) . "\n";
// 出力: 20240213_153045
$targetDate = strtotime('+3 days +5 hours');
echo DateTimeFormatter::formatCountdown($targetDate) . "\n";
// 出力: 残り 3日 05時間 00分
例3: ログフォーマッター
class LogFormatter {
private static $levels = [
'DEBUG' => 'DEBUG',
'INFO' => 'INFO ',
'WARN' => 'WARN ',
'ERROR' => 'ERROR',
];
public static function format($level, $message, $context = []) {
$timestamp = date('Y-m-d H:i:s');
$levelStr = self::$levels[$level] ?? 'UNKNOWN';
$log = sprintf("[%s] [%s] %s", $timestamp, $levelStr, $message);
if (!empty($context)) {
$contextStr = json_encode($context, JSON_UNESCAPED_UNICODE);
$log .= sprintf(" | Context: %s", $contextStr);
}
return $log;
}
public static function formatError($message, $file, $line) {
return sprintf("[ERROR] %s (File: %s, Line: %d)",
$message,
basename($file),
$line
);
}
public static function formatAccess($ip, $method, $path, $status, $time) {
return sprintf("%s - %s %s - Status: %d - Time: %.3fms",
$ip,
$method,
$path,
$status,
$time * 1000
);
}
}
// 使用例
echo LogFormatter::format('INFO', 'アプリケーション起動') . "\n";
// [2024-02-13 15:30:45] [INFO ] アプリケーション起動
echo LogFormatter::format('ERROR', 'データベース接続エラー', [
'host' => 'localhost',
'port' => 3306
]) . "\n";
// [2024-02-13 15:30:45] [ERROR] データベース接続エラー | Context: {"host":"localhost","port":3306}
echo LogFormatter::formatError('Undefined variable', '/path/to/file.php', 42) . "\n";
// [ERROR] Undefined variable (File: file.php, Line: 42)
echo LogFormatter::formatAccess('192.168.1.1', 'GET', '/api/users', 200, 0.0234) . "\n";
// 192.168.1.1 - GET /api/users - Status: 200 - Time: 23.400ms
例4: テーブル表示
class TableFormatter {
public static function displayTable($headers, $rows) {
// カラム幅を計算
$widths = [];
foreach ($headers as $i => $header) {
$widths[$i] = strlen($header);
}
foreach ($rows as $row) {
foreach ($row as $i => $cell) {
$widths[$i] = max($widths[$i], strlen($cell));
}
}
// ヘッダーを表示
$headerFormat = '| ';
foreach ($widths as $width) {
$headerFormat .= "%-{$width}s | ";
}
$headerFormat = rtrim($headerFormat) . "\n";
echo vsprintf($headerFormat, $headers);
// 区切り線
echo '+';
foreach ($widths as $width) {
echo str_repeat('-', $width + 2) . '+';
}
echo "\n";
// データ行を表示
foreach ($rows as $row) {
echo vsprintf($headerFormat, $row);
}
}
public static function displayAlignedTable($data) {
$format = "| %-15s | %10s | %8s |\n";
echo sprintf($format, "名前", "金額", "数量");
echo "+" . str_repeat("-", 15 + 2) . "+" .
str_repeat("-", 10 + 2) . "+" .
str_repeat("-", 8 + 2) . "+\n";
foreach ($data as $row) {
echo sprintf($format, $row['name'],
number_format($row['price']) . '円',
$row['quantity']
);
}
}
}
// 使用例
$headers = ['ID', '名前', 'メール', '登録日'];
$rows = [
['001', '田中太郎', 'tanaka@example.com', '2024-01-15'],
['002', '佐藤花子', 'sato@example.com', '2024-02-01'],
['003', '鈴木一郎', 'suzuki@example.com', '2024-02-10'],
];
TableFormatter::displayTable($headers, $rows);
/*
| ID | 名前 | メール | 登録日 |
+-----+----------+----------------------+------------+
| 001 | 田中太郎 | tanaka@example.com | 2024-01-15 |
| 002 | 佐藤花子 | sato@example.com | 2024-02-01 |
| 003 | 鈴木一郎 | suzuki@example.com | 2024-02-10 |
*/
echo "\n";
$products = [
['name' => 'ノートPC', 'price' => 150000, 'quantity' => 5],
['name' => 'マウス', 'price' => 2500, 'quantity' => 20],
['name' => 'キーボード', 'price' => 8000, 'quantity' => 10],
];
TableFormatter::displayAlignedTable($products);
例5: ファイル名の生成
class FileNameGenerator {
public static function generateBackupName($originalName) {
$timestamp = time();
$dateStr = date('Ymd_His', $timestamp);
$pathInfo = pathinfo($originalName);
return sprintf("%s_backup_%s.%s",
$pathInfo['filename'],
$dateStr,
$pathInfo['extension']
);
}
public static function generateSequentialName($baseName, $number, $extension) {
return sprintf("%s_%04d.%s", $baseName, $number, $extension);
}
public static function generateThumbnailName($originalName, $width, $height) {
$pathInfo = pathinfo($originalName);
return sprintf("%s_thumb_%dx%d.%s",
$pathInfo['filename'],
$width,
$height,
$pathInfo['extension']
);
}
public static function generateVersionedName($name, $version) {
$pathInfo = pathinfo($name);
return sprintf("%s_v%s.%s",
$pathInfo['filename'],
$version,
$pathInfo['extension']
);
}
}
// 使用例
echo FileNameGenerator::generateBackupName('document.pdf') . "\n";
// 出力: document_backup_20240213_153045.pdf
echo FileNameGenerator::generateSequentialName('image', 42, 'jpg') . "\n";
// 出力: image_0042.jpg
echo FileNameGenerator::generateThumbnailName('photo.jpg', 150, 150) . "\n";
// 出力: photo_thumb_150x150.jpg
echo FileNameGenerator::generateVersionedName('app.js', '2.1.3') . "\n";
// 出力: app_v2.1.3.js
例6: URLの生成
class UrlBuilder {
private $baseUrl;
public function __construct($baseUrl) {
$this->baseUrl = rtrim($baseUrl, '/');
}
public function buildUserUrl($userId) {
return sprintf("%s/users/%d", $this->baseUrl, $userId);
}
public function buildProductUrl($category, $productId) {
return sprintf("%s/products/%s/%d",
$this->baseUrl,
urlencode($category),
$productId
);
}
public function buildApiUrl($endpoint, $version = 'v1') {
return sprintf("%s/api/%s/%s",
$this->baseUrl,
$version,
ltrim($endpoint, '/')
);
}
public function buildSearchUrl($query, $page = 1, $perPage = 20) {
return sprintf("%s/search?q=%s&page=%d&per_page=%d",
$this->baseUrl,
urlencode($query),
$page,
$perPage
);
}
public static function buildQueryString($params) {
$parts = [];
foreach ($params as $key => $value) {
$parts[] = sprintf("%s=%s",
urlencode($key),
urlencode($value)
);
}
return implode('&', $parts);
}
}
// 使用例
$builder = new UrlBuilder('https://example.com');
echo $builder->buildUserUrl(123) . "\n";
// https://example.com/users/123
echo $builder->buildProductUrl('electronics', 456) . "\n";
// https://example.com/products/electronics/456
echo $builder->buildApiUrl('users/profile') . "\n";
// https://example.com/api/v1/users/profile
echo $builder->buildSearchUrl('PHPチュートリアル', 2, 30) . "\n";
// https://example.com/search?q=PHP%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB&page=2&per_page=30
$queryString = UrlBuilder::buildQueryString([
'name' => '田中',
'age' => 30,
'city' => '東京'
]);
echo "?" . $queryString . "\n";
// ?name=%E7%94%B0%E4%B8%AD&age=30&city=%E6%9D%B1%E4%BA%AC
例7: プログレスバーの表示
class ProgressBar {
private $total;
private $current = 0;
private $width = 50;
public function __construct($total, $width = 50) {
$this->total = $total;
$this->width = $width;
}
public function update($current) {
$this->current = $current;
$this->display();
}
public function increment() {
$this->current++;
$this->display();
}
private function display() {
$percentage = ($this->current / $this->total) * 100;
$filled = (int)(($this->current / $this->total) * $this->width);
$empty = $this->width - $filled;
$bar = sprintf("[%s%s] %3d%% (%d/%d)",
str_repeat("=", $filled),
str_repeat(" ", $empty),
(int)$percentage,
$this->current,
$this->total
);
echo "\r" . $bar;
if ($this->current >= $this->total) {
echo "\n";
}
}
public static function displayWithETA($current, $total, $startTime) {
$percentage = ($current / $total) * 100;
$elapsed = time() - $startTime;
if ($current > 0) {
$estimated = ($elapsed / $current) * $total;
$remaining = $estimated - $elapsed;
$etaStr = sprintf("%02d:%02d:%02d",
floor($remaining / 3600),
floor(($remaining % 3600) / 60),
$remaining % 60
);
} else {
$etaStr = "--:--:--";
}
return sprintf("進捗: %3d%% | 経過: %ds | 残り: %s",
(int)$percentage,
$elapsed,
$etaStr
);
}
}
// 使用例(実際の動作をシミュレート)
echo "ファイル処理中:\n";
$progress = new ProgressBar(100);
for ($i = 1; $i <= 100; $i++) {
$progress->update($i);
usleep(20000); // 0.02秒待機
}
echo "\nダウンロード進捗:\n";
$startTime = time();
for ($i = 0; $i <= 100; $i += 10) {
echo ProgressBar::displayWithETA($i, 100, $startTime) . "\n";
sleep(1);
}
高度なフォーマット指定
引数の位置指定
// 引数の順序を指定
echo sprintf("%2\$s は %1\$d 歳です", 30, "田中") . "\n";
// 出力: 田中 は 30 歳です
// 同じ引数を複数回使用
echo sprintf("%1\$s さん、こんにちは %1\$s さん!", "田中") . "\n";
// 出力: 田中 さん、こんにちは 田中 さん!
パディング文字の指定
// スペース以外でパディング('フラグで文字を指定)
echo sprintf("%'*10s", "hello") . "\n"; // *****hello
echo sprintf("%'010d", 42) . "\n"; // 0000000042
echo sprintf("%'.-10s", "test") . "\n"; // test......
符号の表示
// 正の数にも符号を表示(+フラグ)
echo sprintf("%+d", 123) . "\n"; // +123
echo sprintf("%+d", -456) . "\n"; // -456
// 符号の位置を確保(スペースフラグ)
echo sprintf("% d", 123) . "\n"; // 123
echo sprintf("% d", -456) . "\n"; // -456
16進数の接頭辞
// 16進数に0xを付ける(#フラグ)
echo sprintf("%#x", 255) . "\n"; // 0xff
echo sprintf("%#X", 255) . "\n"; // 0XFF
// 8進数に0を付ける
echo sprintf("%#o", 8) . "\n"; // 010
vsprintf()との違い
// sprintf(): 個別の引数
$result1 = sprintf("名前: %s, 年齢: %d", "田中", 30);
// vsprintf(): 配列で引数を渡す
$args = ["田中", 30];
$result2 = vsprintf("名前: %s, 年齢: %d", $args);
echo $result1 . "\n";
echo $result2 . "\n";
// 両方とも: 名前: 田中, 年齢: 30
// 配列がある場合はvsprintfが便利
$userData = [
'name' => '佐藤',
'age' => 25,
'city' => '東京'
];
echo vsprintf("名前: %s, 年齢: %d, 都市: %s", array_values($userData)) . "\n";
printf()との違い
// printf(): 直接出力
printf("こんにちは、%sさん\n", "田中");
// 出力: こんにちは、田中さん
// sprintf(): 文字列として取得
$message = sprintf("こんにちは、%sさん", "佐藤");
echo $message . "\n";
// 出力: こんにちは、佐藤さん
// 戻り値
$length = printf("テスト"); // 出力して文字数を返す
echo "\n文字数: " . $length . "\n"; // 4
$str = sprintf("テスト"); // 文字列を返す
echo "文字列: " . $str . "\n";
実用的なヘルパー関数
class StringFormatter {
// 価格フォーマット
public static function price($amount, $currency = '円') {
return sprintf("¥%s%s", number_format($amount), $currency);
}
// パーセンテージ
public static function percentage($value, $decimals = 1) {
return sprintf("%.{$decimals}f%%", $value);
}
// ファイルサイズ
public static function fileSize($bytes) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return sprintf("%.2f %s", $bytes, $units[$i]);
}
// 電話番号
public static function phone($number) {
if (strlen($number) === 10) {
return sprintf("%03d-%04d-%04d",
substr($number, 0, 3),
substr($number, 3, 4),
substr($number, 7, 4)
);
} elseif (strlen($number) === 11) {
return sprintf("%03d-%04d-%04d",
substr($number, 0, 3),
substr($number, 3, 4),
substr($number, 7, 4)
);
}
return $number;
}
// クレジットカード番号(マスキング)
public static function creditCard($number, $maskChar = '*') {
$last4 = substr($number, -4);
$masked = str_repeat($maskChar, strlen($number) - 4);
return sprintf("%s%s", $masked, $last4);
}
}
// 使用例
echo StringFormatter::price(1234567) . "\n";
// ¥1,234,567円
echo StringFormatter::percentage(85.678, 2) . "\n";
// 85.68%
echo StringFormatter::fileSize(1536000) . "\n";
// 1.46 MB
echo StringFormatter::phone('0312345678') . "\n";
// 03-1234-5678
echo StringFormatter::creditCard('1234567890123456') . "\n";
// ************3456
まとめ
sprintf()関数の特徴をまとめると:
できること:
- 文字列のフォーマット指定
- 数値のゼロ埋め・幅揃え
- 小数点以下の桁数指定
- 様々な進数への変換
- 引数の位置指定と再利用
よく使う型指定子:
%s: 文字列%d: 整数%f: 浮動小数点数%.2f: 小数点以下2桁%04d: 4桁ゼロ埋め
関連関数:
printf(): 直接出力vsprintf(): 配列で引数を渡すvprintf(): 配列で引数を渡して出力
推奨される使用場面:
- ログメッセージの整形
- テーブル表示
- ファイル名の生成
- 金額・日時のフォーマット
- URL・クエリ文字列の生成
sprintf()は非常に強力で柔軟性の高い関数です。適切に使いこなすことで、可読性が高く保守しやすいコードを書くことができます!
