[PHP]printf関数の使い方を徹底解説!書式指定付き出力をマスター

PHP

PHPで文字列を出力する際、単にechoで表示するだけでなく、数値のフォーマットや桁揃えなど、きれいに整形して表示したい場合があります。この記事では、PHPのprintf関数について、基本的な使い方から実践的な書式指定まで詳しく解説していきます。

printf関数とは?

printfは、書式文字列に従ってフォーマットされた文字列を出力する関数です。C言語のprintf関数と同様の動作をし、数値の桁数指定、小数点以下の桁数、ゼロ埋めなど、柔軟な書式指定が可能です。

基本的な構文

int printf(string $format, mixed ...$values)

パラメータ:

  • $format: 書式文字列(書式指定子を含む)
  • ...$values: 書式指定子に対応する値(可変長引数)

戻り値:

  • 出力された文字列の長さ

関連関数:

  • sprintf(): フォーマットした文字列を返す(出力しない)
  • fprintf(): ファイルに出力
  • vprintf(): 配列を引数として受け取る

基本的な使い方

シンプルな文字列の出力

<?php
// 基本的な使い方
printf("Hello, World!");
// 出力: Hello, World!

// 変数を埋め込む
$name = "太郎";
printf("こんにちは、%sさん!", $name);
// 出力: こんにちは、太郎さん!

// 複数の変数
$firstName = "山田";
$lastName = "太郎";
printf("%s %s", $firstName, $lastName);
// 出力: 山田 太郎
?>

printfとsprintfの違い

<?php
// printf: 直接出力
printf("Hello, %s!", "World");
// 出力: Hello, World!

// sprintf: 文字列として返す
$message = sprintf("Hello, %s!", "World");
echo $message;
// 出力: Hello, World!

// sprintfは変数に格納できる
$formatted = sprintf("合計: %d円", 1000);
$result = $formatted; // 後で使える
?>

書式指定子の種類

主な書式指定子

指定子説明
%s文字列“Hello”
%d10進数の整数123
%f浮動小数点数123.456
%e科学的記数法1.23e+2
%x16進数(小文字)7b
%X16進数(大文字)7B
%o8進数173
%b2進数1111011
%cASCIIコード値に対応する文字A
%%パーセント記号そのもの%
<?php
// 文字列
printf("名前: %s\n", "太郎");

// 整数
printf("年齢: %d歳\n", 25);

// 浮動小数点数
printf("価格: %f円\n", 1234.56);

// 16進数
printf("色コード: #%X\n", 255);

// 2進数
printf("バイナリ: %b\n", 15);

// パーセント記号
printf("消費税: %d%%\n", 10);
?>

詳細な書式指定

小数点以下の桁数指定

<?php
// 小数点以下2桁
printf("%.2f\n", 123.456);
// 出力: 123.46(四捨五入される)

// 小数点以下4桁
printf("%.4f\n", 3.14159);
// 出力: 3.1416

// 価格表示
$price = 1234.5;
printf("価格: ¥%.2f\n", $price);
// 出力: 価格: ¥1234.50
?>

桁数指定とパディング

<?php
// 右寄せ(デフォルト)、幅10文字
printf("%10s\n", "Hello");
// 出力: "     Hello"

// 左寄せ、幅10文字
printf("%-10s\n", "Hello");
// 出力: "Hello     "

// 数値を右寄せ、幅5桁
printf("%5d\n", 42);
// 出力: "   42"

// ゼロ埋め、幅5桁
printf("%05d\n", 42);
// 出力: "00042"

// 小数点以下2桁、全体で8文字
printf("%8.2f\n", 123.4);
// 出力: "  123.40"
?>

符号の表示

<?php
// 常に符号を表示
printf("%+d\n", 42);   // 出力: +42
printf("%+d\n", -42);  // 出力: -42

// 正の数の場合はスペース
printf("% d\n", 42);   // 出力: " 42"
printf("% d\n", -42);  // 出力: "-42"

// 金額表示で符号を付ける
$balance = 15000;
printf("残高: %+d円\n", $balance);
// 出力: 残高: +15000円
?>

パディング文字の指定

<?php
// ゼロ埋め
printf("%05d\n", 42);
// 出力: 00042

// スペース埋め(デフォルト)
printf("%5d\n", 42);
// 出力: "   42"

// カスタムパディング文字(スペースの代わりに'0'以外)
printf("%'*5s\n", "Hi");
// 出力: "***Hi"

// アンダースコアで埋める
printf("%'_10s\n", "Test");
// 出力: "______Test"
?>

引数の位置指定

<?php
// 通常の順序
printf("%s %s", "Hello", "World");
// 出力: Hello World

// 引数の位置を指定(1から始まる)
printf("%2\$s %1\$s", "World", "Hello");
// 出力: Hello World

// 同じ引数を複数回使用
printf("%1\$s %1\$s %1\$s", "Ha");
// 出力: Ha Ha Ha

// 実用例:日付フォーマット
printf("%3\$d年%1\$02d月%2\$02d日", 12, 25, 2024);
// 出力: 2024年12月25日
?>

実践的な使用例

1. テーブル形式のデータ表示

<?php
/**
 * データをテーブル形式で表示
 */
function displayTable($data) {
    // ヘッダー
    printf("%-5s %-20s %10s %8s\n", "ID", "商品名", "価格", "在庫");
    printf("%s\n", str_repeat("-", 50));
    
    // データ行
    foreach ($data as $row) {
        printf(
            "%-5d %-20s %10.2f %8d\n",
            $row['id'],
            $row['name'],
            $row['price'],
            $row['stock']
        );
    }
    
    // 合計
    $totalPrice = array_sum(array_column($data, 'price'));
    $totalStock = array_sum(array_column($data, 'stock'));
    
    printf("%s\n", str_repeat("-", 50));
    printf("%-26s %10.2f %8d\n", "合計", $totalPrice, $totalStock);
}

// 使用例
$products = [
    ['id' => 1, 'name' => 'ノートPC', 'price' => 89800, 'stock' => 15],
    ['id' => 2, 'name' => 'マウス', 'price' => 2980, 'stock' => 120],
    ['id' => 3, 'name' => 'キーボード', 'price' => 8900, 'stock' => 45],
    ['id' => 4, 'name' => 'モニター', 'price' => 35800, 'stock' => 8],
];

displayTable($products);

/* 出力:
ID    商品名                     価格     在庫
--------------------------------------------------
1     ノートPC                  89800.00       15
2     マウス                     2980.00      120
3     キーボード                 8900.00       45
4     モニター                  35800.00        8
--------------------------------------------------
合計                           137480.00      188
*/
?>

2. 進捗バーの表示

<?php
/**
 * 進捗バーを表示
 */
class ProgressBar {
    private $total;
    private $width;
    
    public function __construct($total, $width = 50) {
        $this->total = $total;
        $this->width = $width;
    }
    
    public function display($current) {
        $percent = ($current / $this->total) * 100;
        $filled = floor(($current / $this->total) * $this->width);
        $empty = $this->width - $filled;
        
        $bar = str_repeat('█', $filled) . str_repeat('░', $empty);
        
        printf(
            "\r[%s] %6.2f%% (%d/%d)",
            $bar,
            $percent,
            $current,
            $this->total
        );
        
        if ($current >= $this->total) {
            echo "\n";
        }
    }
}

// 使用例
$progress = new ProgressBar(100, 40);

for ($i = 0; $i <= 100; $i++) {
    $progress->display($i);
    usleep(50000); // 0.05秒待機
}

/* 出力例:
[████████████████████████████████████████]  100.00% (100/100)
*/
?>

3. 金額表示のフォーマット

<?php
/**
 * 金額フォーマットクラス
 */
class CurrencyFormatter {
    
    /**
     * 日本円フォーマット
     */
    public static function formatJPY($amount, $showSymbol = true) {
        if ($showSymbol) {
            return sprintf("¥%s", number_format($amount));
        }
        return sprintf("%s円", number_format($amount));
    }
    
    /**
     * 米ドルフォーマット
     */
    public static function formatUSD($amount) {
        return sprintf("$%.2f", $amount);
    }
    
    /**
     * 会計形式(負の値を括弧で表示)
     */
    public static function formatAccounting($amount) {
        if ($amount < 0) {
            return sprintf("(¥%s)", number_format(abs($amount)));
        }
        return sprintf(" ¥%s ", number_format($amount));
    }
    
    /**
     * パーセンテージ表示
     */
    public static function formatPercentage($value, $decimals = 2) {
        return sprintf("%." . $decimals . "f%%", $value);
    }
    
    /**
     * 損益表示(色付き)
     */
    public static function formatProfitLoss($amount) {
        if ($amount > 0) {
            return sprintf("+¥%s", number_format($amount));
        } elseif ($amount < 0) {
            return sprintf("-¥%s", number_format(abs($amount)));
        }
        return sprintf("¥%s", number_format($amount));
    }
}

// 使用例
echo "=== 金額フォーマット例 ===\n";
printf("価格: %s\n", CurrencyFormatter::formatJPY(1234567));
printf("価格: %s\n", CurrencyFormatter::formatUSD(1234.56));
printf("利益: %s\n", CurrencyFormatter::formatProfitLoss(15000));
printf("損失: %s\n", CurrencyFormatter::formatProfitLoss(-8000));
printf("割引率: %s\n", CurrencyFormatter::formatPercentage(15.5));
printf("借方: %s\n", CurrencyFormatter::formatAccounting(-50000));

/* 出力:
=== 金額フォーマット例 ===
価格: ¥1,234,567
価格: $1234.56
利益: +¥15,000
損失: -¥8,000
割引率: 15.50%
借方: (¥50,000)
*/
?>

4. ログ出力のフォーマット

<?php
/**
 * ログフォーマッタークラス
 */
class LogFormatter {
    
    const LEVEL_DEBUG = 'DEBUG';
    const LEVEL_INFO = 'INFO';
    const LEVEL_WARN = 'WARN';
    const LEVEL_ERROR = 'ERROR';
    
    /**
     * ログメッセージをフォーマット
     */
    public static function format($level, $message, $context = []) {
        $timestamp = date('Y-m-d H:i:s');
        
        // レベルを固定幅で表示
        printf(
            "[%s] %-5s %s",
            $timestamp,
            $level,
            $message
        );
        
        // コンテキスト情報があれば追加
        if (!empty($context)) {
            printf(" %s", json_encode($context));
        }
        
        echo "\n";
    }
    
    public static function debug($message, $context = []) {
        self::format(self::LEVEL_DEBUG, $message, $context);
    }
    
    public static function info($message, $context = []) {
        self::format(self::LEVEL_INFO, $message, $context);
    }
    
    public static function warn($message, $context = []) {
        self::format(self::LEVEL_WARN, $message, $context);
    }
    
    public static function error($message, $context = []) {
        self::format(self::LEVEL_ERROR, $message, $context);
    }
}

// 使用例
LogFormatter::debug('アプリケーション起動');
LogFormatter::info('ユーザーログイン', ['user_id' => 123, 'ip' => '192.168.1.1']);
LogFormatter::warn('高負荷検出', ['cpu' => 85.5, 'memory' => 78.2]);
LogFormatter::error('データベース接続失敗', ['error' => 'Connection timeout']);

/* 出力:
[2024-12-20 15:30:45] DEBUG アプリケーション起動
[2024-12-20 15:30:45] INFO  ユーザーログイン {"user_id":123,"ip":"192.168.1.1"}
[2024-12-20 15:30:45] WARN  高負荷検出 {"cpu":85.5,"memory":78.2}
[2024-12-20 15:30:45] ERROR データベース接続失敗 {"error":"Connection timeout"}
*/
?>

5. レポート生成

<?php
/**
 * 月次レポート生成クラス
 */
class MonthlyReport {
    
    public static function generate($data) {
        $report = [];
        
        // タイトル
        $report[] = sprintf("\n%s", str_repeat("=", 60));
        $report[] = sprintf("%s", self::center("月次売上レポート", 60));
        $report[] = sprintf("%s", self::center(date('Y年m月'), 60));
        $report[] = sprintf("%s\n", str_repeat("=", 60));
        
        // サマリー
        $report[] = "【サマリー】";
        $report[] = sprintf("  総売上:     %15s", self::formatMoney($data['total_sales']));
        $report[] = sprintf("  総コスト:   %15s", self::formatMoney($data['total_cost']));
        $report[] = sprintf("  純利益:     %15s", self::formatMoney($data['net_profit']));
        $report[] = sprintf("  利益率:     %14.2f%%", $data['profit_margin']);
        $report[] = "";
        
        // 部門別売上
        $report[] = "【部門別売上】";
        $report[] = sprintf("  %-20s %15s %10s", "部門", "売上", "構成比");
        $report[] = sprintf("  %s", str_repeat("-", 50));
        
        foreach ($data['departments'] as $dept) {
            $report[] = sprintf(
                "  %-20s %15s %9.1f%%",
                $dept['name'],
                self::formatMoney($dept['sales']),
                $dept['percentage']
            );
        }
        
        $report[] = "";
        
        // 前月比
        $report[] = "【前月比較】";
        $report[] = sprintf(
            "  売上:   %15s (%+.1f%%)",
            self::formatMoney($data['mom_sales_change']),
            $data['mom_sales_percentage']
        );
        $report[] = sprintf(
            "  利益:   %15s (%+.1f%%)",
            self::formatMoney($data['mom_profit_change']),
            $data['mom_profit_percentage']
        );
        
        $report[] = sprintf("\n%s\n", str_repeat("=", 60));
        
        return implode("\n", $report);
    }
    
    private static function formatMoney($amount) {
        return sprintf("¥%s", number_format($amount));
    }
    
    private static function center($text, $width) {
        $padding = ($width - mb_strlen($text)) / 2;
        return str_repeat(" ", floor($padding)) . $text;
    }
}

// 使用例
$reportData = [
    'total_sales' => 15600000,
    'total_cost' => 10800000,
    'net_profit' => 4800000,
    'profit_margin' => 30.77,
    'departments' => [
        ['name' => '営業部', 'sales' => 8500000, 'percentage' => 54.5],
        ['name' => '技術部', 'sales' => 4800000, 'percentage' => 30.8],
        ['name' => 'サポート部', 'sales' => 2300000, 'percentage' => 14.7],
    ],
    'mom_sales_change' => 1200000,
    'mom_sales_percentage' => 8.3,
    'mom_profit_change' => 450000,
    'mom_profit_percentage' => 10.3,
];

echo MonthlyReport::generate($reportData);

/* 出力:
============================================================
                     月次売上レポート
                       2024年12月
============================================================

【サマリー】
  総売上:         ¥15,600,000
  総コスト:       ¥10,800,000
  純利益:          ¥4,800,000
  利益率:                30.77%

【部門別売上】
  部門                         売上     構成比
  --------------------------------------------------
  営業部                ¥8,500,000      54.5%
  技術部                ¥4,800,000      30.8%
  サポート部            ¥2,300,000      14.7%

【前月比較】
  売上:        ¥1,200,000 (+8.3%)
  利益:          ¥450,000 (+10.3%)

============================================================
*/
?>

6. IPアドレスとバイナリ表示

<?php
/**
 * ネットワーク情報フォーマッター
 */
class NetworkFormatter {
    
    /**
     * IPアドレスを様々な形式で表示
     */
    public static function displayIP($ip) {
        $long = ip2long($ip);
        
        printf("IPアドレス情報:\n");
        printf("  ドット10進表記: %s\n", $ip);
        printf("  32ビット整数:   %u\n", $long);
        printf("  16進数:         0x%08X\n", $long);
        printf("  2進数:          %032b\n", $long);
        
        // オクテットごとに表示
        $octets = explode('.', $ip);
        printf("  オクテット:\n");
        foreach ($octets as $i => $octet) {
            printf(
                "    [%d] %3d = 0x%02X = %08b\n",
                $i,
                $octet,
                $octet,
                $octet
            );
        }
    }
    
    /**
     * MACアドレスをフォーマット
     */
    public static function formatMAC($mac) {
        // 様々な区切り文字を統一
        $mac = strtoupper(preg_replace('/[^0-9A-F]/i', '', $mac));
        
        printf("MACアドレス形式:\n");
        printf("  コロン区切り:   %s\n", chunk_split($mac, 2, ':'));
        printf("  ハイフン区切り: %s\n", chunk_split($mac, 2, '-'));
        printf("  ドット区切り:   %s\n", chunk_split($mac, 4, '.'));
    }
}

// 使用例
NetworkFormatter::displayIP('192.168.1.100');
echo "\n";
NetworkFormatter::formatMAC('00:1A:2B:3C:4D:5E');

/* 出力:
IPアドレス情報:
  ドット10進表記: 192.168.1.100
  32ビット整数:   3232235876
  16進数:         0xC0A80164
  2進数:          11000000101010000000000101100100
  オクテット:
    [0] 192 = 0xC0 = 11000000
    [1] 168 = 0xA8 = 10101000
    [2]   1 = 0x01 = 00000001
    [3] 100 = 0x64 = 01100100

MACアドレス形式:
  コロン区切り:   00:1A:2B:3C:4D:5E:
  ハイフン区切り: 00-1A-2B-3C-4D-5E-
  ドット区切り:   001A.2B3C.4D5E.
*/
?>

よくある間違いと注意点

間違い1: 引数の数が合わない

<?php
// ❌ エラー: 引数が足りない
printf("%s %s", "Hello");
// Warning: printf(): Too few arguments

// ✅ 正しい
printf("%s %s", "Hello", "World");
?>

間違い2: 型が合わない

<?php
// ❌ 文字列を整数として扱う
printf("%d", "abc"); // 出力: 0

// ✅ 適切な型を使用
printf("%s", "abc"); // 出力: abc
printf("%d", 123);   // 出力: 123
?>

間違い3: パーセント記号のエスケープ忘れ

<?php
// ❌ エラー: %を書式指定子と解釈される
printf("消費税は%dです", 10);
// Warning: printf(): Too few arguments

// ✅ %%でエスケープ
printf("消費税は%d%%です", 10);
// 出力: 消費税は10%です
?>

まとめ

printf関数は、PHPで書式付き出力を行うための強力なツールです。以下のポイントを押さえておきましょう。

  • 書式指定子で様々な形式の出力が可能(%s、%d、%fなど)
  • 桁数指定でデータを綺麗に整列表示
  • ゼロ埋めやパディングで見やすい表示を実現
  • 小数点以下の桁数を指定して数値を整形
  • sprintf()で文字列として取得可能
  • テーブル表示やレポート生成に最適
  • ログ出力の統一フォーマットに活用
  • 引数の数と型に注意してエラーを防ぐ

printfを使いこなすことで、データの可読性が大幅に向上し、プロフェッショナルな出力が実現できます。レポート生成やログ出力など、実務でも頻繁に使用する重要な関数です!

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