こんにちは!今回は、PHPの標準関数であるstrchr()について詳しく解説していきます。文字列内で特定の文字を検索し、その文字以降の部分文字列を取得できる便利な関数です!
strchr関数とは?
strchr()関数は、文字列内で特定の文字が最初に現れる位置を検索し、その文字から文字列の最後までを返す関数です。
実はstrchr()はstrstr()のエイリアス(別名)で、全く同じ動作をします。文字列内の部分文字列を抽出する際に便利です!
基本的な構文
strchr(string $haystack, string $needle, bool $before_needle = false): string|false
- $haystack: 検索対象の文字列
- $needle: 検索する文字または文字列
- $before_needle: true の場合、見つかった文字の前の部分を返す(PHP 5.3.0以降)
- 戻り値:
- 見つかった場合: その文字から最後までの部分文字列
- 見つからない場合:
false
基本的な使用例
シンプルな検索
$email = "user@example.com";
// @ 以降を取得
$domain = strchr($email, '@');
echo $domain . "\n";
// 出力: @example.com
// @ より前を取得
$username = strchr($email, '@', true);
echo $username . "\n";
// 出力: user
文字列での検索
$text = "Hello World! Welcome to PHP.";
// 'World' 以降を取得
$result = strchr($text, 'World');
echo $result . "\n";
// 出力: World! Welcome to PHP.
// 'World' より前を取得
$before = strchr($text, 'World', true);
echo $before . "\n";
// 出力: Hello
見つからない場合
$text = "Hello World";
$result = strchr($text, 'xyz');
if ($result === false) {
echo "文字列が見つかりません\n";
} else {
echo "見つかりました: {$result}\n";
}
// 出力: 文字列が見つかりません
実践的な使用例
例1: メールアドレスの処理
class EmailParser {
public static function extractDomain($email) {
// @ 以降を取得してドメイン部分を抽出
$domainPart = strchr($email, '@');
if ($domainPart === false) {
return null;
}
// @ を除去
return substr($domainPart, 1);
}
public static function extractUsername($email) {
// @ より前を取得
return strchr($email, '@', true);
}
public static function maskEmail($email) {
$username = strchr($email, '@', true);
$domain = strchr($email, '@');
if ($username === false || $domain === false) {
return $email;
}
// ユーザー名の一部をマスク
$visibleLength = min(3, strlen($username));
$masked = substr($username, 0, $visibleLength) .
str_repeat('*', strlen($username) - $visibleLength);
return $masked . $domain;
}
public static function getTopLevelDomain($email) {
$domain = self::extractDomain($email);
if ($domain === null) {
return null;
}
// 最後の . 以降を取得
$tld = strchr($domain, '.');
if ($tld === false) {
return null;
}
return substr($tld, 1); // . を除去
}
public static function isCompanyEmail($email, $companyDomain) {
$domain = self::extractDomain($email);
return $domain !== null &&
strcasecmp($domain, $companyDomain) === 0;
}
}
// 使用例
$email = "john.doe@example.com";
echo "ドメイン: " . EmailParser::extractDomain($email) . "\n";
// 出力: example.com
echo "ユーザー名: " . EmailParser::extractUsername($email) . "\n";
// 出力: john.doe
echo "マスク済み: " . EmailParser::maskEmail($email) . "\n";
// 出力: joh*****@example.com
echo "TLD: " . EmailParser::getTopLevelDomain($email) . "\n";
// 出力: com
var_dump(EmailParser::isCompanyEmail($email, 'example.com'));
// 出力: bool(true)
例2: ファイルパスの処理
class PathParser {
public static function getExtension($filename) {
// 最後の . 以降を取得
$ext = strchr($filename, '.');
if ($ext === false) {
return '';
}
return substr($ext, 1); // . を除去
}
public static function getFilename($path) {
// 最後の / または \ 以降を取得
$filename = strchr($path, '/');
if ($filename === false) {
$filename = strchr($path, '\\');
}
if ($filename === false) {
return $path;
}
return substr($filename, 1); // / または \ を除去
}
public static function getDirectory($path) {
// 最後の / または \ より前を取得
$lastSlash = strrpos($path, '/');
$lastBackslash = strrpos($path, '\\');
$pos = max($lastSlash, $lastBackslash);
if ($pos === false) {
return '';
}
return substr($path, 0, $pos);
}
public static function changeExtension($filename, $newExtension) {
$dotPos = strrpos($filename, '.');
if ($dotPos === false) {
return $filename . '.' . $newExtension;
}
return substr($filename, 0, $dotPos) . '.' . $newExtension;
}
public static function removeExtension($filename) {
$ext = strchr($filename, '.');
if ($ext === false) {
return $filename;
}
return strchr($filename, '.', true);
}
}
// 使用例
$path = "/var/www/html/images/photo.jpg";
echo "拡張子: " . PathParser::getExtension($path) . "\n";
// 出力: jpg
echo "ファイル名: " . PathParser::getFilename($path) . "\n";
// 出力: photo.jpg
echo "ディレクトリ: " . PathParser::getDirectory($path) . "\n";
// 出力: /var/www/html/images
echo "拡張子変更: " . PathParser::changeExtension($path, 'png') . "\n";
// 出力: /var/www/html/images/photo.png
echo "拡張子削除: " . PathParser::removeExtension('document.pdf') . "\n";
// 出力: document
例3: URLの解析
class UrlParser {
public static function extractProtocol($url) {
// :// より前を取得
$protocol = strchr($url, '://', true);
return $protocol !== false ? $protocol : null;
}
public static function extractHost($url) {
// プロトコル部分を削除
$afterProtocol = strchr($url, '://');
if ($afterProtocol === false) {
$afterProtocol = $url;
} else {
$afterProtocol = substr($afterProtocol, 3); // :// を削除
}
// / より前を取得(パスがある場合)
$host = strchr($afterProtocol, '/', true);
if ($host === false) {
$host = $afterProtocol;
}
return $host;
}
public static function extractPath($url) {
// プロトコルとホストを削除
$afterProtocol = strchr($url, '://');
if ($afterProtocol === false) {
$afterProtocol = $url;
} else {
$afterProtocol = substr($afterProtocol, 3);
}
// 最初の / 以降を取得
$path = strchr($afterProtocol, '/');
if ($path === false) {
return '/';
}
// クエリ文字列がある場合は除去
$pathWithoutQuery = strchr($path, '?', true);
return $pathWithoutQuery !== false ? $pathWithoutQuery : $path;
}
public static function extractQueryString($url) {
// ? 以降を取得
$query = strchr($url, '?');
if ($query === false) {
return '';
}
// # がある場合は除去(フラグメント)
$queryWithoutFragment = strchr($query, '#', true);
return substr(
$queryWithoutFragment !== false ? $queryWithoutFragment : $query,
1
); // ? を除去
}
public static function extractFragment($url) {
// # 以降を取得
$fragment = strchr($url, '#');
if ($fragment === false) {
return '';
}
return substr($fragment, 1); // # を除去
}
}
// 使用例
$url = "https://example.com:8080/path/to/page?key=value&foo=bar#section";
echo "プロトコル: " . UrlParser::extractProtocol($url) . "\n";
// 出力: https
echo "ホスト: " . UrlParser::extractHost($url) . "\n";
// 出力: example.com:8080
echo "パス: " . UrlParser::extractPath($url) . "\n";
// 出力: /path/to/page
echo "クエリ: " . UrlParser::extractQueryString($url) . "\n";
// 出力: key=value&foo=bar
echo "フラグメント: " . UrlParser::extractFragment($url) . "\n";
// 出力: section
例4: CSVデータの処理
class CsvParser {
public static function parseSimpleLine($line, $delimiter = ',') {
$values = [];
$current = $line;
while (strlen($current) > 0) {
$nextDelimiter = strchr($current, $delimiter);
if ($nextDelimiter === false) {
// 最後の値
$values[] = trim($current);
break;
}
// デリミタより前の値を取得
$value = strchr($current, $delimiter, true);
$values[] = trim($value);
// デリミタの次から継続
$current = substr($nextDelimiter, 1);
}
return $values;
}
public static function extractColumn($lines, $columnIndex, $delimiter = ',') {
$columnData = [];
foreach ($lines as $line) {
$values = self::parseSimpleLine($line, $delimiter);
if (isset($values[$columnIndex])) {
$columnData[] = $values[$columnIndex];
}
}
return $columnData;
}
}
// 使用例
$csvLine = "田中,太郎,30,東京";
$values = CsvParser::parseSimpleLine($csvLine);
print_r($values);
// Array ( [0] => 田中 [1] => 太郎 [2] => 30 [3] => 東京 )
$csvLines = [
"名前,年齢,都市",
"田中,30,東京",
"佐藤,25,大阪",
"鈴木,35,名古屋"
];
$names = CsvParser::extractColumn($csvLines, 0);
print_r($names);
// Array ( [0] => 名前 [1] => 田中 [2] => 佐藤 [3] => 鈴木 )
例5: ログの解析
class LogParser {
public static function parseApacheLog($line) {
// IPアドレスを抽出(最初のスペースまで)
$ip = strchr($line, ' ', true);
// タイムスタンプを抽出([ と ] の間)
$timestampStart = strchr($line, '[');
$timestamp = strchr($timestampStart, ']', true);
$timestamp = substr($timestamp, 1); // [ を除去
// HTTPメソッドとパスを抽出(" の間)
$requestStart = strchr($line, '"');
$request = strchr(substr($requestStart, 1), '"', true);
// ステータスコードを抽出
$afterRequest = strchr($line, '" ');
$statusPart = substr($afterRequest, 2); // " を除去
$status = (int)strchr($statusPart, ' ', true);
return [
'ip' => $ip,
'timestamp' => $timestamp,
'request' => $request,
'status' => $status
];
}
public static function parseCustomLog($line) {
// [日時] レベル: メッセージ 形式
// タイムスタンプを抽出
$timestampPart = strchr($line, '[');
$timestamp = strchr($timestampPart, ']', true);
$timestamp = substr($timestamp, 1);
// レベルを抽出
$afterTimestamp = strchr($line, '] ');
$levelPart = substr($afterTimestamp, 2);
$level = strchr($levelPart, ':', true);
// メッセージを抽出
$message = strchr($levelPart, ': ');
$message = substr($message, 2);
return [
'timestamp' => $timestamp,
'level' => trim($level),
'message' => trim($message)
];
}
}
// 使用例
$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] ERROR: Database connection failed';
$parsed = LogParser::parseCustomLog($customLog);
print_r($parsed);
例6: コマンドライン引数の処理
class CommandParser {
public static function parseCommand($commandLine) {
// コマンド名を抽出(最初のスペースまで)
$command = strchr($commandLine, ' ', true);
if ($command === false) {
$command = $commandLine;
$args = '';
} else {
// 引数部分を取得
$args = strchr($commandLine, ' ');
$args = trim($args);
}
return [
'command' => $command,
'arguments' => $args
];
}
public static function extractOption($args, $option) {
// --option=value 形式を検索
$optionPattern = "--{$option}=";
$optionPos = strpos($args, $optionPattern);
if ($optionPos === false) {
return null;
}
$afterOption = substr($args, $optionPos + strlen($optionPattern));
// 次のスペースまたは文字列の最後まで
$value = strchr($afterOption, ' ', true);
return $value !== false ? $value : $afterOption;
}
public static function extractFlag($args, $flag) {
// -f または --flag 形式を検索
$shortFlag = "-{$flag}";
$longFlag = "--{$flag}";
return strpos($args, $shortFlag) !== false ||
strpos($args, $longFlag) !== false;
}
}
// 使用例
$commandLine = "git commit -m \"Initial commit\" --author=\"John Doe\"";
$parsed = CommandParser::parseCommand($commandLine);
print_r($parsed);
// command => git, arguments => commit -m "Initial commit" --author="John Doe"
$args = 'process --input=data.txt --output=result.txt --verbose';
echo "input: " . CommandParser::extractOption($args, 'input') . "\n";
// 出力: data.txt
echo "output: " . CommandParser::extractOption($args, 'output') . "\n";
// 出力: result.txt
var_dump(CommandParser::extractFlag($args, 'verbose'));
// 出力: bool(true)
例7: HTML/XMLタグの処理
class TagParser {
public static function extractTagContent($html, $tagName) {
$openTag = "<{$tagName}>";
$closeTag = "</{$tagName}>";
// 開始タグを検索
$afterOpen = strchr($html, $openTag);
if ($afterOpen === false) {
return null;
}
// 開始タグを除去
$content = substr($afterOpen, strlen($openTag));
// 終了タグより前を取得
$result = strchr($content, $closeTag, true);
return $result !== false ? $result : null;
}
public static function extractAttribute($tag, $attribute) {
// attribute="value" または attribute='value' 形式
$pattern = "{$attribute}=\"";
$afterAttr = strchr($tag, $pattern);
if ($afterAttr === false) {
$pattern = "{$attribute}='";
$afterAttr = strchr($tag, $pattern);
}
if ($afterAttr === false) {
return null;
}
// 引用符を除去
$valueStart = substr($afterAttr, strlen($pattern));
// 次の引用符まで
$value = strchr($valueStart, '"', true);
if ($value === false) {
$value = strchr($valueStart, "'", true);
}
return $value;
}
public static function removeHtmlTags($html) {
$result = $html;
while (($tagStart = strchr($result, '<')) !== false) {
$tagEnd = strchr($tagStart, '>');
if ($tagEnd === false) {
break;
}
// タグの前と後を結合
$before = strchr($result, '<', true);
$after = substr($tagEnd, 1);
$result = $before . $after;
}
return $result;
}
}
// 使用例
$html = '<div class="container"><p>Hello World</p></div>';
echo TagParser::extractTagContent($html, 'p') . "\n";
// 出力: Hello World
$tag = '<img src="photo.jpg" alt="My Photo" width="200">';
echo "src: " . TagParser::extractAttribute($tag, 'src') . "\n";
// 出力: photo.jpg
echo "alt: " . TagParser::extractAttribute($tag, 'alt') . "\n";
// 出力: My Photo
echo TagParser::removeHtmlTags('<p>Hello <b>World</b>!</p>') . "\n";
// 出力: Hello World!
strstr()との関係
// strchr()とstrstr()は完全に同じ
$email = "user@example.com";
$result1 = strchr($email, '@');
$result2 = strstr($email, '@');
var_dump($result1 === $result2); // bool(true)
// どちらを使っても同じ結果
echo strchr($email, '@') . "\n"; // @example.com
echo strstr($email, '@') . "\n"; // @example.com
注意点と制限事項
最初に見つかった位置のみ
$text = "apple banana apple orange";
// 最初の 'apple' 以降を取得
$result = strchr($text, 'apple');
echo $result . "\n";
// 出力: apple banana apple orange
// 2番目の 'apple' は検索されない
大文字小文字の区別
$text = "Hello World";
// 大文字小文字を区別する
$result1 = strchr($text, 'world'); // false(見つからない)
$result2 = strchr($text, 'World'); // World(見つかる)
var_dump($result1); // bool(false)
echo $result2 . "\n"; // World
// 大文字小文字を無視する場合は stristr()
$result3 = stristr($text, 'world');
echo $result3 . "\n"; // World
マルチバイト文字の扱い
$text = "こんにちは世界";
// strchr()はバイト単位で動作
$result = strchr($text, '世界');
echo $result . "\n"; // 世界
// マルチバイト対応が必要な場合は mb_strstr()
$result = mb_strstr($text, '世界');
echo $result . "\n"; // 世界
パフォーマンスの考慮
// 方法1: strchr()
$email = "user@example.com";
$domain = strchr($email, '@');
// 方法2: explode()
$parts = explode('@', $email);
$domain = '@' . $parts[1];
// 方法3: substr() + strpos()
$pos = strpos($email, '@');
$domain = substr($email, $pos);
// strchr()は最もシンプルで、単純な検索には効率的
まとめ
strchr()関数の特徴をまとめると:
できること:
- 文字列内で文字を検索
- 見つかった位置以降の部分文字列を取得
- 見つかった位置より前の部分文字列を取得(第3引数)
strstr()との関係:
strchr()はstrstr()の完全なエイリアス- 機能的に全く同じ
推奨される使用場面:
- メールアドレスの解析
- ファイルパスの処理
- URLの解析
- ログファイルの解析
- 区切り文字での分割
関連関数:
strstr(): strchr()と同じstristr(): 大文字小文字を区別しない検索strrchr(): 最後に現れた位置から検索strpos(): 位置(インデックス)を返すmb_strstr(): マルチバイト対応版
注意点:
- 最初に見つかった位置のみ
- 大文字小文字を区別する
- マルチバイト文字列には注意が必要
- 見つからない場合は
falseを返す
strchr()は文字列の一部を抽出する際に便利な関数です。特にメールアドレスやファイルパスなど、特定の区切り文字で分割されたデータを扱う場合に効果的です!
