[PHP]sprintf関数を完全解説!フォーマット文字列で出力を整形する方法

PHP

こんにちは!今回は、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”
%b2進数sprintf(“%b”, 5) → “101”
%o8進数sprintf(“%o”, 8) → “10”
%x16進数(小文字)sprintf(“%x”, 255) → “ff”
%X16進数(大文字)sprintf(“%X”, 255) → “FF”
%cASCII文字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()は非常に強力で柔軟性の高い関数です。適切に使いこなすことで、可読性が高く保守しやすいコードを書くことができます!

タイトルとURLをコピーしました