文字列の関数
Nette\Utils\Strings は、UTF-8 でエンコードされた文字列を扱う便利な関数を集めた静的クラスです。
インストール:
composer require nette/utils
以下の例では、次のクラスの別名が定義されているものとします。
use Nette\Utils\Strings;
大文字と小文字
これらの関数には PHP の mbstring 拡張が必要です。
lower (string $s): string
UTF-8 の文字列を小文字に変換します。
Strings::lower('Hello World'); // 'hello world'
upper (string $s): string
UTF-8 の文字列を大文字に変換します。
Strings::upper('Hello World'); // 'HELLO WORLD'
firstUpper (string $s): string
UTF-8 の文字列の最初の文字を大文字にし、ほかの文字はそのままにします。
Strings::firstUpper('hello world'); // 'Hello world'
firstLower (string $s): string
UTF-8 の文字列の最初の文字を小文字にし、ほかの文字はそのままにします。
Strings::firstLower('Hello world'); // 'hello world'
capitalize (string $s): string
UTF-8 の文字列の各単語の最初の文字を大文字にし、ほかを小文字にします。
Strings::capitalize('hello world'); // 'Hello World'
文字列の加工
normalize (string $s): string
制御文字を取り除き、改行を \n
に正規化し、先頭と末尾の空行を取り除き、各行の末尾の空白を取り除き、UTF-8 を NFC
正規形に正規化します。
unixNewLines (string $s): string
改行を Unix 系システムで使う \n に変換します。改行とみなされるのは
\n、\r、\r\n、U+2028 行区切り、U+2029 段落区切りです。
$unixLikeLines = Strings::unixNewLines($string);
platformNewLines (string $s): string
改行を現在のプラットフォーム固有の文字、つまり Windows では \r\n、それ以外では
\n に変換します。改行とみなされるのは \n、\r、\r\n、U+2028
行区切り、U+2029 段落区切りです。
$platformLines = Strings::platformNewLines($string);
webalize (string $s, ?string $charlist=null, bool $lower=true): string
UTF-8 の文字列を URL で使う形に整えます。つまりダイアクリティカルマークを取り除き、英語のアルファベットと数字以外のすべての文字をハイフンに置き換えます。
Strings::webalize('žluťoučký kůň'); // 'zlutoucky-kun'
ほかの文字を残したい場合は、第 2 パラメータで指定できます。
Strings::webalize('10. image_id', '._'); // '10.-image_id'
第 3 パラメータで小文字への変換を抑えられます。
Strings::webalize('Dobrý den', null, false); // 'Dobry-den'
PHP の intl 拡張が必要です。
trim (string $s, string $charlist=self::TrimCharacters): string
UTF-8 の文字列の先頭と末尾から空白(または第 2 パラメータで指定した文字)を取り除きます。
Strings::trim(' Hello '); // 'Hello'
truncate (string $s, int $maxLen,
string $append=`'…'`): string
単語を丸ごと保とうとしながら、UTF-8 の文字列を指定した最大長に切り詰めます。切り詰められた場合は末尾に省略記号(第 3 パラメータで変えられます)が付きます。
$text = 'Hello, how are you today?';
Strings::truncate($text, 5); // 'Hell…'
Strings::truncate($text, 20); // 'Hello, how are you…'
Strings::truncate($text, 30); // 'Hello, how are you today?'
Strings::truncate($text, 20, '~'); // 'Hello, how are you~'
indent (string $s, int $level=1, string
$chars=`"\t"`): string
複数行のテキストを左からインデントします。第 2 パラメータはインデント文字の数、第 3 パラメータはインデントに使う文字(既定はタブ)を指定します。
Strings::indent('Nette'); // "\tNette"
Strings::indent('Nette', 2, '+'); // '++Nette'
padLeft (string $s, int $length, string
$pad=`' '`): string
UTF-8 の文字列の左に $pad を足して、指定した長さまで埋めます。
Strings::padLeft('Nette', 6); // ' Nette'
Strings::padLeft('Nette', 8, '+*'); // '+*+Nette'
padRight (string $s, int $length,
string $pad=`' '`): string
UTF-8 の文字列の右に $pad を足して、指定した長さまで埋めます。
Strings::padRight('Nette', 6); // 'Nette '
Strings::padRight('Nette', 8, '+*'); // 'Nette+*+'
substring (string $s, int $start, ?int $length=null): string
開始位置 $start と長さ $length で指定した UTF-8 文字列 $s
の一部を返します。$start が負なら、返される文字列は末尾から $start
番目の文字から始まります。
Strings::substring('Nette Framework', 0, 5); // 'Nette'
Strings::substring('Nette Framework', 6); // 'Framework'
Strings::substring('Nette Framework', -4); // 'work'
reverse (string $s): string
UTF-8 の文字列を逆順にします。
Strings::reverse('Nette'); // 'etteN'
length (string $s): int
UTF-8 の文字列の文字数(バイト数ではありません)を返します。
これは Unicode のコードポイントの数で、書記素の数とは異なることがあります。
Strings::length('Nette'); // 5
Strings::length('červená'); // 7
startsWith (string $haystack, string $needle): bool
文字列 $haystack が文字列 $needle で始まるかを調べます。
$haystack = 'Starts';
$needle = 'St';
Strings::startsWith($haystack, $needle); // true
ネイティブの str_starts_with()
関数を使ってください。
endsWith (string $haystack, string $needle): bool
文字列 $haystack が文字列 $needle で終わるかを調べます。
$haystack = 'Ends';
$needle = 'ds';
Strings::endsWith($haystack, $needle); // true
ネイティブの str_ends_with()
関数を使ってください。
contains (string $haystack, string $needle): bool
文字列 $haystack が文字列 $needle を含むかを調べます。
$haystack = 'Auditorium';
$needle = 'dit';
Strings::contains($haystack, $needle); // true
ネイティブの str_contains()
関数を使ってください。
compare (string $left, string $right, ?int $length=null): bool
2 つの UTF-8 文字列、またはその一部を大文字小文字を区別せずに比較します。$length
が null
なら文字列全体を比較します。負なら文字列の末尾からその数の文字を比較します。それ以外なら先頭からその数の文字を比較します。
Strings::compare('Nette', 'nette'); // true
Strings::compare('Nette', 'next', 2); // true - 最初の 2 文字が一致
Strings::compare('Nette', 'Latte', -2); // true - 最後の 2 文字が一致
findPrefix (array $strings): string
文字列たちの共通の接頭辞を見つけます。共通の接頭辞がなければ空文字列を返します。
Strings::findPrefix(['prefix-a', 'prefix-bb', 'prefix-c']); // 'prefix-'
Strings::findPrefix(['Nette', 'is', 'great']); // ''
before (string $haystack, string $needle, int $nth=1): ?string
文字列 $haystack の中で、文字列 $needle の $nth
番目の出現より前の部分を返します。$needle が見つからなければ null
を返します。$nth が負なら文字列の末尾から探します。
Strings::before('Nette_is_great', '_', 1); // 'Nette'
Strings::before('Nette_is_great', '_', -2); // 'Nette'
Strings::before('Nette_is_great', ' '); // null
Strings::before('Nette_is_great', '_', 3); // null
after (string $haystack, string $needle, int $nth=1): ?string
文字列 $haystack の中で、文字列 $needle の $nth
番目の出現より後ろの部分を返します。$needle が見つからなければ null
を返します。$nth が負なら文字列の末尾から探します。
Strings::after('Nette_is_great', '_', 2); // 'great'
Strings::after('Nette_is_great', '_', -1); // 'great'
Strings::after('Nette_is_great', ' '); // null
Strings::after('Nette_is_great', '_', 3); // null
indexOf (string $haystack, string $needle, int $nth=1): ?int
文字列 $haystack の中の文字列 $needle の $nth
番目の出現の文字位置を返します。$needle が見つからなければ null
を返します。$nth が負なら文字列の末尾から探します。
Strings::indexOf('abc abc abc', 'abc', 2); // 4
Strings::indexOf('abc abc abc', 'abc', -1); // 8
Strings::indexOf('abc abc abc', 'd'); // null
エンコーディング
fixEncoding (string $s): string
文字列から不正な UTF-8 の文字を取り除きます。
$correctString = Strings::fixEncoding($invalidString);
checkEncoding (string $s): bool
文字列が正しい UTF-8 の文字列かを調べます。
$isUtf8 = Strings::checkEncoding($string);
Nette\Utils\Validators::isUnicode()を使ってください。
toAscii (string $s): string
UTF-8 の文字列を ASCII に変換します。つまりダイアクリティカルマークなどを取り除きます。
Strings::toAscii('žluťoučký kůň'); // 'zlutoucky kun'
PHP の intl 拡張が必要です。
chr (int $code): string
コードポイント(0×0000..D7FF または 0xE000..10FFFF の範囲の数)から、UTF-8 の特定の文字を返します。
Strings::chr(0xA9); // UTF-8 エンコーディングの '©'
ord (string $c): int
UTF-8 の特定の文字のコードポイント(0×0000..D7FF または 0xE000..10FFFF の範囲の数)を返します。
Strings::ord('©'); // 169 (0xA9)
正規表現
Strings クラスは正規表現を扱う関数も提供します。ネイティブの PHP
関数と違い、より分かりやすい API、優れた Unicode
サポート、そして何より重要なエラーの検出を備えています。コンパイルや式の処理でエラーが起きると
Nette\RegexpException を投げます。
split (string $subject, string $pattern, bool $captureOffset=false, bool $skipEmpty=false, int $limit=-1, bool $utf8=false): array
正規表現を使って文字列を配列に分割します。かっこの中の式も取り込まれて返されます。
Strings::split('hello, world', '~,\s*~');
// ['hello', 'world']
Strings::split('hello, world', '~(,)\s*~');
// ['hello', ',', 'world']
$skipEmpty が true なら、空でない要素だけが返されます。
Strings::split('hello, world, ', '~,\s*~');
// ['hello', 'world', '']
Strings::split('hello, world, ', '~,\s*~', skipEmpty: true);
// ['hello', 'world']
$limit
を指定すると、その上限までの部分文字列だけが返され、残りの文字列は最後の要素に入ります。-1
か 0 の上限は制限なしを意味します。
Strings::split('hello, world, third', '~,\s*~', limit: 2);
// ['hello', 'world, third']
$utf8 が true なら、u 修飾子を使うのと同じように Unicode
モードで評価します。
$captureOffset が true
なら、文字列中の各一致の位置も返されます(バイト単位。$utf8
が設定されていれば文字単位)。これにより戻り値は、一致した文字列とその位置の組を要素とする配列に変わります。
Strings::split('žlutý, kůň', '~,\s*~', captureOffset: true);
// [['žlutý', 0], ['kůň', 9]]
Strings::split('žlutý, kůň', '~,\s*~', captureOffset: true, utf8: true);
// [['žlutý', 0], ['kůň', 7]] // 位置は文字単位です
match (string $subject, string $pattern, bool $captureOffset=false, int $offset=0, bool $unmatchedAsNull=false, bool $utf8=false): ?array
文字列の中から正規表現に一致する部分を探し、見つかった式と個々の部分式を含む配列を返します。一致がなければ
null を返します。
Strings::match('hello!', '~\w+(!+)~');
// ['hello!', '!']
Strings::match('hello!', '~X~');
// null
$unmatchedAsNull が true なら、一致しなかった部分パターンは null
として返されます。そうでなければ空文字列として返されるか、まるごと省かれます。
Strings::match('hello', '~\w+(!+)?~');
// ['hello'](省略可能なグループ !+ は一致しませんでした)
Strings::match('hello', '~\w+(!+)?~', unmatchedAsNull: true);
// ['hello', null]
$utf8 が true なら、u 修飾子を使うのと同じように Unicode
モードで評価します。
Strings::match('žlutý kůň', '~\w+~'); // UTF-8 なし
// ['lut'](ASCII の単語文字だけに一致します)
Strings::match('žlutý kůň', '~\w+~', utf8: true); // UTF-8 あり
// ['žlutý'](Unicode の単語文字に一致します)
$offset パラメータで検索の開始位置を指定できます(バイト単位。$utf8
が設定されていれば文字単位)。
$captureOffset が true
なら、文字列中の各一致の位置も返されます(バイト単位。$utf8
が設定されていれば文字単位)。これにより戻り値は、一致した文字列とそのオフセットの組を要素とする配列に変わります。
Strings::match('žlutý!', '~\w+(!+)?~', captureOffset: true); // UTF-8 なし
// [['lut', 2]](ASCII の一致のみ、オフセットはバイト単位)
Strings::match('žlutý!', '~\w+(!+)?~', captureOffset: true, utf8: true); // UTF-8 あり
// [['žlutý!', 0], ['!', 5]](Unicode の一致、オフセットは文字単位)
matchAll (string $subject, string $pattern, bool $captureOffset=false, int $offset=0, bool $unmatchedAsNull=false, bool $patternOrder=false, bool $utf8=false, bool $lazy=false): array|Generator
文字列の中から正規表現に一致するすべての出現を探し、見つかった式と個々の部分式を含む配列の配列を返します。
Strings::matchAll('hello, world!!', '~\w+(!+)?~');
/* [
0 => ['hello'],
1 => ['world!!', '!!'],
] */
$patternOrder が true
なら結果の構造が変わります。最初の要素はパターン全体の一致の配列、2 つめは 1
番目のかっこの部分パターンに一致した文字列の配列、という具合です。
Strings::matchAll('hello, world!!', '~\w+(!+)?~', patternOrder: true);
/* [
0 => ['hello', 'world!!'],
1 => ['', '!!'],
] */
$unmatchedAsNull が true なら、一致しなかった部分パターンは null
として返されます。そうでなければ空文字列として返されるか省かれます。
Strings::matchAll('hello, world!!', '~\w+(!+)?~', unmatchedAsNull: true);
/* [
0 => ['hello', null],
1 => ['world!!', '!!'],
] */
$utf8 が true なら、u 修飾子を使うのと同じように Unicode
モードで評価します。
Strings::matchAll('žlutý kůň', '~\w+~');
/* [
0 => ['lut'],
1 => ['k'],
] */
Strings::matchAll('žlutý kůň', '~\w+~', utf8: true);
/* [
0 => ['žlutý'],
1 => ['kůň'],
] */
$offset パラメータで検索の開始位置を指定できます(バイト単位。$utf8
が設定されていれば文字単位)。
$captureOffset が true
なら、文字列中の各一致の位置も返されます(バイト単位。$utf8
が設定されていれば文字単位)。これにより戻り値の構造が変わり、各一致の要素が
[一致した文字列, 位置] の組になります。
Strings::matchAll('žlutý kůň', '~\w+~', captureOffset: true);
/* [
0 => [['lut', 2]],
1 => [['k', 8]],
] */
Strings::matchAll('žlutý kůň', '~\w+~', captureOffset: true, utf8: true);
/* [
0 => [['žlutý', 0]],
1 => [['kůň', 6]],
] */
$lazy が true なら、この関数は配列ではなく Generator
を返します。文字列全体を一度に処理するのではなく一致を少しずつ見つけていくので、大きな文字列を扱うときの性能が大きく向上します。おかげで非常に大きな入力も効率よく扱えます。さらに、目当ての一致が見つかればいつでも処理を中断でき、計算時間を節約できます。
$matches = Strings::matchAll($largeText, '~\w+~', lazy: true);
foreach ($matches as $match) {
echo "Found: $match[0]\n";
// 処理はいつでも中断できます。たとえば break; で
}
replace (string $subject, string|array
$pattern, string|callable $replacement='', int $limit=-1, bool $captureOffset=false, bool
$unmatchedAsNull=false, bool $utf8=false): string
正規表現に一致するすべての出現を置き換えます。$replacement
は置換文字列のマスクか、コールバック関数です。
Strings::replace('hello, world!', '~\w+~', '--');
// '--, --!'
Strings::replace('hello, world!', '~\w+~', fn($m) => strrev($m[0]));
// 'olleh, dlrow!'
第 2 パラメータに pattern => replacement
の形式の配列を渡すと、複数の置換もできます。
Strings::replace('hello, world!', [
'~\w+~' => '--',
'~,\s+~' => ' ',
]);
// '-- --!'
$limit パラメータは行う置換の数を制限します。-1
の上限は制限なしを意味します。
$utf8 が true なら、u 修飾子を使うのと同じように Unicode
モードで評価します。
Strings::replace('žlutý kůň', '~\w+~', '--');
// 'ž--ý --ůň'
Strings::replace('žlutý kůň', '~\w+~', '--', utf8: true);
// '-- --'
$captureOffset が true
なら、文字列中の各一致の位置(バイト単位。$utf8
が設定されていれば文字単位)もコールバックに渡されます。これにより渡される配列の構造が変わり、各要素が
[一致した文字列, 位置] の組になります。
Strings::replace(
'žlutý kůň',
'~\w+~',
function (array $m) { dump($m); return ''; },
captureOffset: true,
);
// [['lut', 2]] と [['k', 8]] をダンプします
Strings::replace(
'žlutý kůň',
'~\w+~',
function (array $m) { dump($m); return ''; },
captureOffset: true,
utf8: true,
);
// [['žlutý', 0]] と [['kůň', 6]] をダンプします
$unmatchedAsNull が true なら、一致しなかった部分パターンは null
としてコールバックに渡されます。そうでなければ空文字列として渡されるか省かれます。
Strings::replace(
'ac',
'~(a)(b)*(c)~',
function (array $m) { dump($m); return ''; },
);
// ['ac', 'a', '', 'c'] をダンプします
Strings::replace(
'ac',
'~(a)(b)*(c)~',
function (array $m) { dump($m); return ''; },
unmatchedAsNull: true,
);
// ['ac', 'a', null, 'c'] をダンプします