こんにちは!今回は、PHPの標準関数であるsscanf()について詳しく解説していきます。sprintf()の逆の操作で、フォーマット指定に従って文字列から値を抽出できる便利な関数です!
sscanf関数とは?
sscanf()関数は、フォーマット文字列に従って文字列を解析し、値を抽出する関数です。
sprintf()が値を文字列にフォーマットするのに対し、sscanf()は文字列から値を取り出します。ログファイルの解析や、構造化されたテキストデータの処理に非常に便利です!
基本的な構文
// 方法1: 変数に代入
sscanf(string $string, string $format, mixed &...$vars): int|null
// 方法2: 配列として取得
sscanf(string $string, string $format): array|null
- $string: 解析対象の文字列
- $format: フォーマット文字列(変換指定子を含む)
- $vars: 抽出した値を格納する変数(参照渡し)
- 戻り値:
- 引数指定時: 代入された引数の数(int)または失敗時はnull
- 引数省略時: 抽出された値の配列(array)または失敗時はnull
主なフォーマット指定子
| 型指定子 | 説明 | 例 |
|---|---|---|
%s | 文字列(空白まで) | “hello world” → “hello” |
%d | 整数(10進数) | “123” → 123 |
%f | 浮動小数点数 | “3.14” → 3.14 |
%x | 16進数 | “ff” → 255 |
%o | 8進数 | “10” → 8 |
%c | 1文字 | “A” → “A” |
%[...] | 文字集合(正規表現的) | “%[0-9]” → 数字のみ |
%[^...] | 文字集合の否定 | “%[^,]” → カンマ以外 |
基本的な使用例
シンプルな解析
// 方法1: 変数に代入
$str = "田中太郎 30";
sscanf($str, "%s %d", $name, $age);
echo "名前: {$name}\n"; // 田中太郎
echo "年齢: {$age}\n"; // 30
// 方法2: 配列として取得
$result = sscanf($str, "%s %d");
print_r($result);
// Array ( [0] => 田中太郎 [1] => 30 )
日付の解析
$dateStr = "2024-02-13";
sscanf($dateStr, "%d-%d-%d", $year, $month, $day);
echo "年: {$year}\n"; // 2024
echo "月: {$month}\n"; // 2
echo "日: {$day}\n"; // 13
// または
list($year, $month, $day) = sscanf($dateStr, "%d-%d-%d");
echo "日付: {$year}年{$month}月{$day}日\n";
複数形式の解析
// IPアドレスの解析
$ip = "192.168.1.100";
sscanf($ip, "%d.%d.%d.%d", $a, $b, $c, $d);
echo "セグメント: {$a}, {$b}, {$c}, {$d}\n";
// セグメント: 192, 168, 1, 100
// カラーコードの解析
$color = "#FF5733";
sscanf($color, "#%2x%2x%2x", $r, $g, $b);
echo "RGB: ({$r}, {$g}, {$b})\n";
// RGB: (255, 87, 51)
実践的な使用例
例1: ログファイルの解析
class LogParser {
public static function parseApacheLog($line) {
// Apache Combined Log Format
// 192.168.1.1 - - [13/Feb/2024:10:30:45 +0900] "GET /index.html HTTP/1.1" 200 1234
$pattern = '%s - - [%[^]]] "%s %s %[^"]" %d %d';
$result = sscanf($line, $pattern);
if ($result === null || count($result) < 6) {
return null;
}
return [
'ip' => $result[0],
'datetime' => $result[1],
'method' => $result[2],
'path' => $result[3],
'status' => $result[4],
'size' => $result[5]
];
}
public static function parseCustomLog($line) {
// [2024-02-13 10:30:45] INFO: User login - UserID: 123
sscanf($line, "[%[^]]] %[^:]: %[^-] - UserID: %d",
$timestamp, $level, $message, $userId
);
return [
'timestamp' => $timestamp,
'level' => trim($level),
'message' => trim($message),
'user_id' => $userId
];
}
public static function parseErrorLog($line) {
// [error] 2024/02/13 10:30:45 [client 192.168.1.1] File not found: /var/www/html/test.php
$pattern = '[%[^]]] %d/%d/%d %d:%d:%d [client %[^]]] %[^:]';
$result = sscanf($line, $pattern);
if (!$result) {
return null;
}
return [
'level' => $result[0],
'year' => $result[1],
'month' => $result[2],
'day' => $result[3],
'hour' => $result[4],
'minute' => $result[5],
'second' => $result[6],
'client_ip' => $result[7],
'message' => $result[8]
];
}
}
// 使用例
$apacheLog = '192.168.1.1 - - [13/Feb/2024:10:30:45 +0900] "GET /index.html HTTP/1.1" 200 1234';
$parsed = LogParser::parseApacheLog($apacheLog);
print_r($parsed);
$customLog = '[2024-02-13 10:30:45] INFO: User login - UserID: 123';
$parsed = LogParser::parseCustomLog($customLog);
print_r($parsed);
例2: CSVやTSVの解析
class DataParser {
public static function parseCSVLine($line) {
// 簡易的なCSV解析(クォートなし)
$values = sscanf($line, "%[^,],%[^,],%[^,],%d");
return [
'name' => $values[0] ?? '',
'email' => $values[1] ?? '',
'city' => $values[2] ?? '',
'age' => $values[3] ?? 0
];
}
public static function parseKeyValue($line, $separator = '=') {
// key=value 形式の解析
$pattern = "%[^{$separator}]{$separator}%[^\n]";
$result = sscanf($line, $pattern);
if ($result && count($result) === 2) {
return [
'key' => trim($result[0]),
'value' => trim($result[1])
];
}
return null;
}
public static function parseIniLine($line) {
// INIファイルの行を解析
$line = trim($line);
// コメント行をスキップ
if (empty($line) || $line[0] === ';' || $line[0] === '#') {
return null;
}
// セクション
if ($line[0] === '[') {
$result = sscanf($line, "[%[^]]]");
return ['type' => 'section', 'value' => $result[0]];
}
// キー=値
$result = sscanf($line, "%[^=]=%[^\r\n]");
if ($result && count($result) === 2) {
return [
'type' => 'pair',
'key' => trim($result[0]),
'value' => trim($result[1])
];
}
return null;
}
}
// 使用例
$csvLine = "田中太郎,tanaka@example.com,東京,30";
$data = DataParser::parseCSVLine($csvLine);
print_r($data);
$kvLine = "username=tanaka";
$kv = DataParser::parseKeyValue($kvLine);
print_r($kv);
$iniLines = [
"[database]",
"host=localhost",
"port=3306",
"; this is a comment",
"name=mydb"
];
foreach ($iniLines as $line) {
$parsed = DataParser::parseIniLine($line);
if ($parsed) {
print_r($parsed);
}
}
例3: 座標・測定値の解析
class CoordinateParser {
public static function parseLatLng($str) {
// 緯度経度の解析
// 例: "35.6895,139.6917" または "35.6895N,139.6917E"
$result = sscanf($str, "%f,%f");
if ($result && count($result) === 2) {
return [
'latitude' => $result[0],
'longitude' => $result[1]
];
}
// 方位付き形式
$result = sscanf($str, "%f%c,%f%c");
if ($result && count($result) === 4) {
$lat = $result[0] * ($result[1] === 'S' ? -1 : 1);
$lng = $result[2] * ($result[3] === 'W' ? -1 : 1);
return [
'latitude' => $lat,
'longitude' => $lng
];
}
return null;
}
public static function parseGPSData($line) {
// GPS NMEA形式の簡易解析
// $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
$pattern = "\$GPGGA,%f,%f,%c,%f,%c,%d,%d,%f,%f";
$result = sscanf($line, $pattern);
if (!$result || count($result) < 9) {
return null;
}
return [
'time' => $result[0],
'latitude' => $result[1] / 100, // 度分形式を10進数に
'lat_dir' => $result[2],
'longitude' => $result[3] / 100,
'lng_dir' => $result[4],
'quality' => $result[5],
'satellites' => $result[6],
'hdop' => $result[7],
'altitude' => $result[8]
];
}
public static function parseMeasurement($str) {
// 測定値の解析
// 例: "Temperature: 25.5C" または "Pressure: 1013 hPa"
$result = sscanf($str, "%[^:]: %f %s");
if ($result && count($result) === 3) {
return [
'type' => $result[0],
'value' => $result[1],
'unit' => $result[2]
];
}
return null;
}
}
// 使用例
$coords1 = "35.6895,139.6917";
print_r(CoordinateParser::parseLatLng($coords1));
$coords2 = "35.6895N,139.6917E";
print_r(CoordinateParser::parseLatLng($coords2));
$measurement = "Temperature: 25.5 C";
print_r(CoordinateParser::parseMeasurement($measurement));
例4: バージョン番号やIDの解析
class IdentifierParser {
public static function parseVersion($version) {
// セマンティックバージョニング
// 例: "2.1.3" または "2.1.3-beta.1"
$result = sscanf($version, "%d.%d.%d");
if (!$result || count($result) < 3) {
return null;
}
$parsed = [
'major' => $result[0],
'minor' => $result[1],
'patch' => $result[2]
];
// プレリリース情報があれば抽出
if (strpos($version, '-') !== false) {
$parts = explode('-', $version, 2);
$parsed['prerelease'] = $parts[1];
}
return $parsed;
}
public static function parseProductCode($code) {
// 商品コードの解析
// 例: "ABC-12345-XL-RED"
$result = sscanf($code, "%3s-%5d-%[^-]-%s");
if (!$result || count($result) < 4) {
return null;
}
return [
'category' => $result[0],
'id' => $result[1],
'size' => $result[2],
'color' => $result[3]
];
}
public static function parseISBN($isbn) {
// ISBN-13の解析
// 例: "978-4-12-345678-9"
// ハイフンを削除
$cleanIsbn = str_replace('-', '', $isbn);
if (strlen($cleanIsbn) === 13) {
$result = sscanf($cleanIsbn, "%3d%1d%2d%6d%1d");
if ($result && count($result) === 5) {
return [
'prefix' => $result[0],
'group' => $result[1],
'publisher' => $result[2],
'title' => $result[3],
'check' => $result[4]
];
}
}
return null;
}
public static function parseMACAddress($mac) {
// MACアドレスの解析
// 例: "00:1A:2B:3C:4D:5E" または "00-1A-2B-3C-4D-5E"
$mac = strtoupper($mac);
// コロン区切り
$result = sscanf($mac, "%2x:%2x:%2x:%2x:%2x:%2x");
if (!$result || count($result) !== 6) {
// ハイフン区切り
$result = sscanf($mac, "%2x-%2x-%2x-%2x-%2x-%2x");
}
if ($result && count($result) === 6) {
return [
'bytes' => $result,
'vendor' => sprintf("%02X:%02X:%02X", $result[0], $result[1], $result[2])
];
}
return null;
}
}
// 使用例
$version = "2.1.3-beta.1";
print_r(IdentifierParser::parseVersion($version));
$productCode = "ABC-12345-XL-RED";
print_r(IdentifierParser::parseProductCode($productCode));
$isbn = "978-4-12-345678-9";
print_r(IdentifierParser::parseISBN($isbn));
$mac = "00:1A:2B:3C:4D:5E";
print_r(IdentifierParser::parseMACAddress($mac));
例5: ファイル名・パスの解析
class PathParser {
public static function parseFilename($filename) {
// ファイル名のパターン解析
// 例: "document_2024-02-13_v1.2.pdf"
$result = sscanf($filename, "%[^_]_%d-%d-%d_v%f.%s");
if ($result && count($result) >= 6) {
return [
'basename' => $result[0],
'year' => $result[1],
'month' => $result[2],
'day' => $result[3],
'version' => $result[4],
'extension' => $result[5]
];
}
return null;
}
public static function parseImageFilename($filename) {
// 画像ファイル名のパターン
// 例: "IMG_20240213_103045.jpg" または "photo_001_thumb_150x150.png"
// パターン1: カメラ形式
$result = sscanf($filename, "IMG_%8d_%6d.%s");
if ($result && count($result) === 3) {
return [
'type' => 'camera',
'date' => $result[0],
'time' => $result[1],
'extension' => $result[2]
];
}
// パターン2: サムネイル形式
$result = sscanf($filename, "%[^_]_%d_thumb_%dx%d.%s");
if ($result && count($result) === 5) {
return [
'type' => 'thumbnail',
'basename' => $result[0],
'number' => $result[1],
'width' => $result[2],
'height' => $result[3],
'extension' => $result[4]
];
}
return null;
}
public static function parseLogFilename($filename) {
// ログファイル名
// 例: "app_2024-02-13.log" または "error_20240213_103045.log"
$result = sscanf($filename, "%[^_]_%d-%d-%d.%s");
if ($result && count($result) === 5) {
return [
'type' => $result[0],
'year' => $result[1],
'month' => $result[2],
'day' => $result[3],
'extension' => $result[4]
];
}
$result = sscanf($filename, "%[^_]_%8d_%6d.%s");
if ($result && count($result) === 4) {
return [
'type' => $result[0],
'date' => $result[1],
'time' => $result[2],
'extension' => $result[3]
];
}
return null;
}
}
// 使用例
$filename1 = "document_2024-02-13_v1.2.pdf";
print_r(PathParser::parseFilename($filename1));
$filename2 = "IMG_20240213_103045.jpg";
print_r(PathParser::parseImageFilename($filename2));
$filename3 = "photo_001_thumb_150x150.png";
print_r(PathParser::parseImageFilename($filename3));
$filename4 = "app_2024-02-13.log";
print_r(PathParser::parseLogFilename($filename4));
例6: ネットワークデータの解析
class NetworkParser {
public static function parseHTTPHeader($header) {
// HTTPヘッダーの解析
// 例: "HTTP/1.1 200 OK"
$result = sscanf($header, "HTTP/%f %d %[^\r\n]");
if ($result && count($result) === 3) {
return [
'version' => $result[0],
'status_code' => $result[1],
'status_text' => $result[2]
];
}
// ヘッダーフィールド
// 例: "Content-Type: text/html; charset=UTF-8"
$result = sscanf($header, "%[^:]: %[^\r\n]");
if ($result && count($result) === 2) {
return [
'field' => trim($result[0]),
'value' => trim($result[1])
];
}
return null;
}
public static function parseURL($url) {
// URLの解析(簡易版)
// 例: "https://example.com:8080/path/to/page?key=value"
$result = sscanf($url, "%[^:]://%[^:]:%d/%[^?]?%s");
if ($result && count($result) === 5) {
return [
'scheme' => $result[0],
'host' => $result[1],
'port' => $result[2],
'path' => $result[3],
'query' => $result[4]
];
}
// ポートなしの場合
$result = sscanf($url, "%[^:]://%[^/]/%[^?]?%s");
if ($result && count($result) === 4) {
return [
'scheme' => $result[0],
'host' => $result[1],
'path' => $result[2],
'query' => $result[3]
];
}
return null;
}
public static function parseUserAgent($ua) {
// User-Agentの簡易解析
// Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
$result = sscanf($ua, "Mozilla/%f (%[^)]) %[^/]/%f");
if ($result && count($result) === 4) {
return [
'mozilla_version' => $result[0],
'platform' => $result[1],
'engine' => $result[2],
'engine_version' => $result[3]
];
}
return null;
}
}
// 使用例
$httpStatus = "HTTP/1.1 200 OK";
print_r(NetworkParser::parseHTTPHeader($httpStatus));
$httpHeader = "Content-Type: text/html; charset=UTF-8";
print_r(NetworkParser::parseHTTPHeader($httpHeader));
$url1 = "https://example.com:8080/path/to/page?key=value";
print_r(NetworkParser::parseURL($url1));
$url2 = "https://example.com/path?query=test";
print_r(NetworkParser::parseURL($url2));
例7: センサーデータの解析
class SensorDataParser {
public static function parseTemperatureReading($data) {
// 温度センサーデータ
// 例: "TEMP:25.5C HUMIDITY:60%"
$temp = sscanf($data, "TEMP:%f%c HUMIDITY:%d%%");
if ($temp && count($temp) === 3) {
return [
'temperature' => $temp[0],
'temp_unit' => $temp[1],
'humidity' => $temp[2]
];
}
return null;
}
public static function parseAccelerometer($data) {
// 加速度センサーデータ
// 例: "ACC:X=0.15,Y=-0.23,Z=9.81"
$result = sscanf($data, "ACC:X=%f,Y=%f,Z=%f");
if ($result && count($result) === 3) {
return [
'x' => $result[0],
'y' => $result[1],
'z' => $result[2],
'magnitude' => sqrt(
$result[0]**2 +
$result[1]**2 +
$result[2]**2
)
];
}
return null;
}
public static function parseBatteryStatus($data) {
// バッテリー状態
// 例: "BAT:85% VOLTAGE:3.7V TEMP:28C"
$result = sscanf($data, "BAT:%d%% VOLTAGE:%f%c TEMP:%d%c");
if ($result && count($result) === 5) {
return [
'percentage' => $result[0],
'voltage' => $result[1],
'voltage_unit' => $result[2],
'temperature' => $result[3],
'temp_unit' => $result[4]
];
}
return null;
}
public static function parseTimestampedData($data) {
// タイムスタンプ付きデータ
// 例: "[2024-02-13 10:30:45.123] SENSOR_01: VALUE=123.45"
$pattern = "[%d-%d-%d %d:%d:%f] %[^:]: VALUE=%f";
$result = sscanf($data, $pattern);
if ($result && count($result) === 8) {
return [
'timestamp' => sprintf(
"%04d-%02d-%02d %02d:%02d:%06.3f",
$result[0], $result[1], $result[2],
$result[3], $result[4], $result[5]
),
'sensor_id' => $result[6],
'value' => $result[7]
];
}
return null;
}
}
// 使用例
$tempData = "TEMP:25.5C HUMIDITY:60%";
print_r(SensorDataParser::parseTemperatureReading($tempData));
$accData = "ACC:X=0.15,Y=-0.23,Z=9.81";
print_r(SensorDataParser::parseAccelerometer($accData));
$batData = "BAT:85% VOLTAGE:3.7V TEMP:28C";
print_r(SensorDataParser::parseBatteryStatus($batData));
$tsData = "[2024-02-13 10:30:45.123] SENSOR_01: VALUE=123.45";
print_r(SensorDataParser::parseTimestampedData($tsData));
文字集合の指定
正規表現的なパターン
// 数字のみ抽出
$str = "ABC123DEF456";
$result = sscanf($str, "%[A-Z]%[0-9]%[A-Z]%[0-9]");
print_r($result);
// Array ( [0] => ABC [1] => 123 [2] => DEF [3] => 456 )
// カンマ以外を抽出(カンマ区切りCSV)
$csv = "田中,太郎,30,東京";
$result = sscanf($csv, "%[^,],%[^,],%[^,],%[^,]");
print_r($result);
// Array ( [0] => 田中 [1] => 太郎 [2] => 30 [3] => 東京 )
// 空白以外を抽出
$str = "hello world test";
$result = sscanf($str, "%[^ ] %[^ ] %[^ ]");
print_r($result);
// Array ( [0] => hello [1] => world [2] => test )
エラーハンドリング
class SafeParser {
public static function parse($string, $format, $expectedCount) {
$result = sscanf($string, $format);
if ($result === null) {
throw new Exception("解析に失敗しました");
}
if (count($result) !== $expectedCount) {
throw new Exception(
sprintf(
"期待される要素数: %d, 実際: %d",
$expectedCount,
count($result)
)
);
}
return $result;
}
public static function parseOrDefault($string, $format, $default = []) {
$result = sscanf($string, $format);
if ($result === null || empty($result)) {
return $default;
}
return $result;
}
}
// 使用例
try {
$data = SafeParser::parse("田中 30", "%s %d", 2);
print_r($data);
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
// デフォルト値を使用
$data = SafeParser::parseOrDefault("invalid data", "%d %d", [0, 0]);
print_r($data);
戻り値の扱い
// パターン1: 変数に代入
$count = sscanf("田中 30", "%s %d", $name, $age);
echo "抽出数: {$count}\n"; // 2
echo "名前: {$name}, 年齢: {$age}\n";
// パターン2: 配列として取得
$data = sscanf("田中 30", "%s %d");
echo "名前: {$data[0]}, 年齢: {$data[1]}\n";
// パターン3: list()と組み合わせ
list($name, $age) = sscanf("田中 30", "%s %d");
echo "名前: {$name}, 年齢: {$age}\n";
preg_match()との比較
// sscanf()の場合
$str = "田中 30";
list($name, $age) = sscanf($str, "%s %d");
// preg_match()の場合
$str = "田中 30";
preg_match('/^(\S+) (\d+)$/', $str, $matches);
$name = $matches[1];
$age = $matches[2];
// sscanf()の利点:
// - シンプルで読みやすい
// - 型変換が自動(%d は整数に変換)
// - フォーマット指定が直感的
// preg_match()の利点:
// - より複雑なパターンマッチング
// - 柔軟性が高い
// - 条件付きマッチング
まとめ
sscanf()関数の特徴をまとめると:
できること:
- フォーマット指定による文字列解析
- 型の自動変換(文字列→整数、浮動小数点数など)
- 複雑なパターンからの値抽出
- ログファイルやデータファイルの解析
主な型指定子:
%s: 文字列%d: 整数%f: 浮動小数点数%x: 16進数%[...]: 文字集合%[^...]: 文字集合の否定
戻り値の取得方法:
- 変数に代入(参照渡し)
- 配列として取得
- list()との組み合わせ
推奨される使用場面:
- ログファイルの解析
- 構造化テキストデータの処理
- 座標や測定値の抽出
- ファイル名やIDのパース
- センサーデータの解析
関連関数:
sprintf(): 値を文字列にフォーマットfscanf(): ファイルから読み込んで解析preg_match(): 正規表現でのマッチング
sscanf()は、固定フォーマットのテキストデータを扱う際に非常に便利です。正規表現よりもシンプルで読みやすく、型変換も自動で行われるため、適切な場面で使うことで効率的なコードを書くことができます!
