はじめに
PHPで文字列を扱う際、文字を数値として処理したい場面に遭遇することがあります。例えば、文字コードの比較、バイナリデータの処理、独自のエンコーディング処理などです。
そんな時に活躍するのが ord
関数です。この記事では、文字をASCIIコード(数値)に変換するこの基本的ながら重要な関数について、基礎から実践的な活用法まで詳しく解説します。
ord関数とは
ord
は、文字列の最初の1バイトを取得し、その値を0〜255の整数として返す関数です。主にASCII文字の数値コードを取得する際に使用されます。
基本構文
int ord(string $character)
パラメータ
- $character (string): 変換したい文字を含む文字列
戻り値
- int: 文字列の最初の1バイトの数値(0〜255)
基本的な使用例
シンプルな文字コード取得
<?php
// アルファベットの文字コード
echo ord('A'); // 出力: 65
echo ord('a'); // 出力: 97
echo ord('Z'); // 出力: 90
echo ord('z'); // 出力: 122
// 数字の文字コード
echo ord('0'); // 出力: 48
echo ord('9'); // 出力: 57
// 記号の文字コード
echo ord(' '); // 出力: 32(スペース)
echo ord('!'); // 出力: 33
echo ord('.'); // 出力: 46
// 改行文字
echo ord("\n"); // 出力: 10
echo ord("\r"); // 出力: 13
echo ord("\t"); // 出力: 9(タブ)
?>
複数文字の処理
<?php
// 文字列の各文字の文字コードを表示
function displayCharCodes($string) {
echo "文字列: \"{$string}\"\n";
echo "文字コード一覧:\n";
for ($i = 0; $i < strlen($string); $i++) {
$char = $string[$i];
$code = ord($char);
echo " '{$char}' => {$code}\n";
}
}
displayCharCodes("Hello");
// 出力:
// 文字列: "Hello"
// 文字コード一覧:
// 'H' => 72
// 'e' => 101
// 'l' => 108
// 'l' => 108
// 'o' => 111
?>
chr関数との関係(逆変換)
<?php
// ord()とchr()は相互変換の関係
$char = 'A';
$code = ord($char); // 65
$back = chr($code); // 'A'
echo "元の文字: {$char}\n";
echo "文字コード: {$code}\n";
echo "逆変換: {$back}\n";
// 文字コードの範囲を確認
echo "\n=== ASCII印字可能文字 ===\n";
for ($i = 32; $i <= 126; $i++) {
echo chr($i) . " ";
if (($i - 31) % 20 == 0) echo "\n";
}
?>
実践的な活用例
1. 文字種の判定
<?php
class CharValidator {
/**
* 大文字アルファベットかチェック
*/
public static function isUppercase($char) {
$code = ord($char);
return $code >= 65 && $code <= 90; // A-Z
}
/**
* 小文字アルファベットかチェック
*/
public static function isLowercase($char) {
$code = ord($char);
return $code >= 97 && $code <= 122; // a-z
}
/**
* アルファベットかチェック
*/
public static function isAlpha($char) {
return self::isUppercase($char) || self::isLowercase($char);
}
/**
* 数字かチェック
*/
public static function isDigit($char) {
$code = ord($char);
return $code >= 48 && $code <= 57; // 0-9
}
/**
* 英数字かチェック
*/
public static function isAlphanumeric($char) {
return self::isAlpha($char) || self::isDigit($char);
}
/**
* 空白文字かチェック
*/
public static function isWhitespace($char) {
$code = ord($char);
// スペース、タブ、改行、キャリッジリターン
return in_array($code, [9, 10, 13, 32]);
}
/**
* 印字可能文字かチェック
*/
public static function isPrintable($char) {
$code = ord($char);
return $code >= 32 && $code <= 126;
}
}
// 使用例
$test_chars = ['A', 'z', '5', ' ', '@', "\n", 'あ'];
foreach ($test_chars as $char) {
echo "文字: '{$char}' (コード: " . ord($char) . ")\n";
echo " 大文字: " . (CharValidator::isUppercase($char) ? 'Yes' : 'No') . "\n";
echo " 小文字: " . (CharValidator::isLowercase($char) ? 'Yes' : 'No') . "\n";
echo " 数字: " . (CharValidator::isDigit($char) ? 'Yes' : 'No') . "\n";
echo " 印字可能: " . (CharValidator::isPrintable($char) ? 'Yes' : 'No') . "\n";
echo "\n";
}
?>
2. シーザー暗号の実装
<?php
class CaesarCipher {
/**
* シーザー暗号で暗号化
* @param string $text 平文
* @param int $shift シフト量
* @return string 暗号文
*/
public static function encrypt($text, $shift = 3) {
$result = '';
for ($i = 0; $i < strlen($text); $i++) {
$char = $text[$i];
$code = ord($char);
if (ord('A') <= $code && $code <= ord('Z')) {
// 大文字の処理
$shifted = (($code - ord('A') + $shift) % 26) + ord('A');
$result .= chr($shifted);
} elseif (ord('a') <= $code && $code <= ord('z')) {
// 小文字の処理
$shifted = (($code - ord('a') + $shift) % 26) + ord('a');
$result .= chr($shifted);
} else {
// アルファベット以外はそのまま
$result .= $char;
}
}
return $result;
}
/**
* シーザー暗号で復号化
* @param string $text 暗号文
* @param int $shift シフト量
* @return string 平文
*/
public static function decrypt($text, $shift = 3) {
// 復号化は負のシフト
return self::encrypt($text, 26 - $shift);
}
/**
* 総当たりで復号化を試みる
* @param string $text 暗号文
* @return array 全パターンの結果
*/
public static function bruteForce($text) {
$results = [];
for ($shift = 0; $shift < 26; $shift++) {
$results[$shift] = self::decrypt($text, $shift);
}
return $results;
}
}
// 使用例
$original = "Hello World!";
$encrypted = CaesarCipher::encrypt($original, 3);
$decrypted = CaesarCipher::decrypt($encrypted, 3);
echo "元のテキスト: {$original}\n";
echo "暗号化: {$encrypted}\n";
echo "復号化: {$decrypted}\n";
// 総当たり攻撃のデモ
echo "\n=== 総当たり攻撃 ===\n";
$attempts = CaesarCipher::bruteForce($encrypted);
foreach ($attempts as $shift => $result) {
echo "シフト {$shift}: {$result}\n";
}
?>
3. 文字列の差分計算
<?php
class StringComparator {
/**
* 2つの文字列の文字コード差分を計算
*/
public static function calculateDifference($str1, $str2) {
$max_len = max(strlen($str1), strlen($str2));
$differences = [];
for ($i = 0; $i < $max_len; $i++) {
$char1 = $i < strlen($str1) ? $str1[$i] : null;
$char2 = $i < strlen($str2) ? $str2[$i] : null;
$code1 = $char1 !== null ? ord($char1) : 0;
$code2 = $char2 !== null ? ord($char2) : 0;
$differences[] = [
'position' => $i,
'char1' => $char1,
'char2' => $char2,
'code1' => $code1,
'code2' => $code2,
'diff' => abs($code1 - $code2),
'same' => $char1 === $char2
];
}
return $differences;
}
/**
* 差分を視覚的に表示
*/
public static function displayDifference($str1, $str2) {
$diffs = self::calculateDifference($str1, $str2);
echo "文字列1: \"{$str1}\"\n";
echo "文字列2: \"{$str2}\"\n";
echo str_repeat("=", 60) . "\n";
foreach ($diffs as $diff) {
$status = $diff['same'] ? '✓' : '✗';
echo sprintf(
"[%d] %s '%s'(%d) vs '%s'(%d) | 差分: %d\n",
$diff['position'],
$status,
$diff['char1'] ?? 'null',
$diff['code1'],
$diff['char2'] ?? 'null',
$diff['code2'],
$diff['diff']
);
}
}
/**
* 文字列の類似度を計算(簡易版)
*/
public static function calculateSimilarity($str1, $str2) {
$diffs = self::calculateDifference($str1, $str2);
$same_count = 0;
foreach ($diffs as $diff) {
if ($diff['same']) {
$same_count++;
}
}
$max_len = max(strlen($str1), strlen($str2));
return $max_len > 0 ? ($same_count / $max_len) * 100 : 0;
}
}
// 使用例
StringComparator::displayDifference("Hello", "Hallo");
echo "\n類似度: " . StringComparator::calculateSimilarity("Hello", "Hallo") . "%\n";
?>
4. チェックサム計算
<?php
class ChecksumCalculator {
/**
* 単純な加算チェックサム
*/
public static function simpleChecksum($data) {
$sum = 0;
for ($i = 0; $i < strlen($data); $i++) {
$sum += ord($data[$i]);
}
return $sum % 256; // 1バイトに収める
}
/**
* XORチェックサム
*/
public static function xorChecksum($data) {
$checksum = 0;
for ($i = 0; $i < strlen($data); $i++) {
$checksum ^= ord($data[$i]);
}
return $checksum;
}
/**
* チェックサム検証
*/
public static function verifyChecksum($data, $expected_checksum, $method = 'simple') {
$calculated = $method === 'xor'
? self::xorChecksum($data)
: self::simpleChecksum($data);
return $calculated === $expected_checksum;
}
/**
* データにチェックサムを付加
*/
public static function appendChecksum($data, $method = 'simple') {
$checksum = $method === 'xor'
? self::xorChecksum($data)
: self::simpleChecksum($data);
return $data . chr($checksum);
}
/**
* チェックサム検証付きデータ抽出
*/
public static function extractAndVerify($data_with_checksum, $method = 'simple') {
if (strlen($data_with_checksum) < 2) {
return ['valid' => false, 'data' => null, 'error' => 'データが短すぎます'];
}
$data = substr($data_with_checksum, 0, -1);
$checksum = ord(substr($data_with_checksum, -1));
$valid = self::verifyChecksum($data, $checksum, $method);
return [
'valid' => $valid,
'data' => $valid ? $data : null,
'checksum' => $checksum,
'error' => $valid ? null : 'チェックサムが一致しません'
];
}
}
// 使用例
$original_data = "Important Data";
// チェックサムを付加
$data_with_checksum = ChecksumCalculator::appendChecksum($original_data);
echo "元データ: {$original_data}\n";
echo "チェックサム付きデータ長: " . strlen($data_with_checksum) . "\n";
echo "チェックサム値: " . ord(substr($data_with_checksum, -1)) . "\n";
// 検証
$result = ChecksumCalculator::extractAndVerify($data_with_checksum);
echo "検証結果: " . ($result['valid'] ? '✓ 正常' : '✗ エラー') . "\n";
echo "抽出データ: {$result['data']}\n";
// データ改ざんのテスト
$tampered_data = "Important Date" . substr($data_with_checksum, -1);
$tampered_result = ChecksumCalculator::extractAndVerify($tampered_data);
echo "\n改ざんデータの検証: " . ($tampered_result['valid'] ? '✓ 正常' : '✗ エラー') . "\n";
echo "エラー: {$tampered_result['error']}\n";
?>
5. バイナリデータの可視化
<?php
class BinaryVisualizer {
/**
* バイナリデータを16進数ダンプで表示
*/
public static function hexDump($data, $bytes_per_line = 16) {
$len = strlen($data);
echo "オフセット ";
for ($i = 0; $i < $bytes_per_line; $i++) {
echo sprintf("%02X ", $i);
}
echo " ASCII\n";
echo str_repeat("-", $bytes_per_line * 3 + 30) . "\n";
for ($i = 0; $i < $len; $i += $bytes_per_line) {
// オフセット表示
echo sprintf("%08X ", $i);
// 16進数表示
for ($j = 0; $j < $bytes_per_line; $j++) {
if ($i + $j < $len) {
echo sprintf("%02X ", ord($data[$i + $j]));
} else {
echo " ";
}
}
echo " ";
// ASCII表示
for ($j = 0; $j < $bytes_per_line && ($i + $j) < $len; $j++) {
$char = $data[$i + $j];
$code = ord($char);
// 印字可能文字のみ表示
echo ($code >= 32 && $code <= 126) ? $char : '.';
}
echo "\n";
}
}
/**
* バイト値の統計情報を表示
*/
public static function analyzeBytes($data) {
$stats = [
'length' => strlen($data),
'min' => 255,
'max' => 0,
'sum' => 0,
'frequency' => array_fill(0, 256, 0)
];
for ($i = 0; $i < $stats['length']; $i++) {
$byte = ord($data[$i]);
$stats['sum'] += $byte;
$stats['min'] = min($stats['min'], $byte);
$stats['max'] = max($stats['max'], $byte);
$stats['frequency'][$byte]++;
}
$stats['average'] = $stats['length'] > 0
? $stats['sum'] / $stats['length']
: 0;
echo "=== バイナリデータ分析 ===\n";
echo "データ長: {$stats['length']} バイト\n";
echo "最小値: {$stats['min']}\n";
echo "最大値: {$stats['max']}\n";
echo "平均値: " . number_format($stats['average'], 2) . "\n";
// 頻出バイト値
arsort($stats['frequency']);
echo "\n頻出バイト値 (上位10個):\n";
$count = 0;
foreach ($stats['frequency'] as $byte => $freq) {
if ($freq > 0 && $count < 10) {
$char = ($byte >= 32 && $byte <= 126) ? chr($byte) : '.';
echo sprintf(" 0x%02X ('%s'): %d回\n", $byte, $char, $freq);
$count++;
}
}
return $stats;
}
}
// 使用例
$sample_data = "Hello, World! This is a binary data sample.\n";
$sample_data .= "It contains various characters: 123 !@#$%";
echo "=== 16進数ダンプ ===\n";
BinaryVisualizer::hexDump($sample_data);
echo "\n";
BinaryVisualizer::analyzeBytes($sample_data);
?>
マルチバイト文字との注意点
<?php
// ord()は最初の1バイトのみを処理
$japanese = "あいうえお";
echo "日本語文字列: {$japanese}\n";
echo "最初の1バイト: " . ord($japanese) . "\n";
echo "文字列長: " . strlen($japanese) . " バイト\n";
echo "文字数: " . mb_strlen($japanese) . " 文字\n\n";
// マルチバイト文字の正しい処理
function getUnicodeCodePoint($char) {
$values = unpack('N', mb_convert_encoding($char, 'UTF-32BE', 'UTF-8'));
return $values[1] ?? null;
}
$chars = ['A', 'あ', '🍎', '€'];
foreach ($chars as $char) {
echo "文字: {$char}\n";
echo " ord(): " . ord($char) . " (最初の1バイトのみ)\n";
echo " Unicodeコードポイント: U+" .
sprintf("%04X", getUnicodeCodePoint($char)) . "\n";
echo "\n";
}
// 推奨: マルチバイト文字を扱う場合
function safeGetByteValues($string) {
$encoding = mb_detect_encoding($string, ['UTF-8', 'ASCII', 'ISO-8859-1'], true);
echo "エンコーディング: {$encoding}\n";
echo "バイト値:\n";
for ($i = 0; $i < strlen($string); $i++) {
echo sprintf(" [%d] 0x%02X (%d)\n", $i, ord($string[$i]), ord($string[$i]));
}
}
safeGetByteValues("Hello");
echo "\n";
safeGetByteValues("こんにちは");
?>
パフォーマンスの考慮事項
<?php
// 大量の文字を処理する場合の最適化
function processStringNaive($string) {
$start = microtime(true);
$result = [];
for ($i = 0; $i < strlen($string); $i++) {
$result[] = ord($string[$i]);
}
$elapsed = microtime(true) - $start;
echo "素朴な方法: " . number_format($elapsed * 1000, 4) . " ms\n";
return $result;
}
function processStringOptimized($string) {
$start = microtime(true);
// unpackを使った高速化
$result = array_values(unpack('C*', $string));
$elapsed = microtime(true) - $start;
echo "最適化版: " . number_format($elapsed * 1000, 4) . " ms\n";
return $result;
}
// ベンチマーク
$test_string = str_repeat("The quick brown fox jumps over the lazy dog. ", 1000);
echo "テスト文字列長: " . strlen($test_string) . " バイト\n\n";
$result1 = processStringNaive($test_string);
$result2 = processStringOptimized($test_string);
echo "\n結果が一致: " . ($result1 === $result2 ? '✓' : '✗') . "\n";
?>
まとめ
ord
関数は、文字とバイト値を橋渡しするPHPの基本的な関数です。
主な用途:
- 文字コードの取得: ASCII/バイナリ値の確認
- 文字種の判定: カスタムバリデーション処理
- 暗号化・エンコーディング: 簡易的な暗号化処理
- チェックサム計算: データ整合性の検証
- バイナリデータ処理: プロトコル実装やデータ解析
注意点:
- 1バイトのみを処理(マルチバイト文字には注意)
- UTF-8などのマルチバイトエンコーディングでは
mb_*
関数を併用 - 大量データ処理には
unpack()
などの最適化を検討
シンプルな関数ですが、低レベルな文字列処理やバイナリデータの扱いにおいて、非常に重要な役割を果たします。基礎をしっかり理解することで、より高度な文字列処理やデータ操作が可能になります。
文字コードの理解は、プログラミングの基礎です。この記事を通じて、PHPでの文字列処理の深い理解につながれば幸いです。