PHPでは、 strnatcasecmp関数を使用して、2つの文字列を比較し、「自然な種類」に従ってケースインスセンシタルを比較します。いわゆる「自然なソート」は、人間の直観のようなソートを意味します。たとえば、 Image2.pngは、通常の辞書の順序ではなく、 Image10.pngの前にランク付けされます。
ファイルパスを処理する場合、開発者はしばしばStrnatcasecmpを使用して2つのファイル名またはパスを比較します。ただし、ファイルパス自体の複雑さにより、この関数は実際の使用で予想外の問題を簡単に引き起こす可能性があります。この記事では、これらの一般的な問題を紹介し、対応するソリューションを提供します。
strnatcasecmpは、パスセパレーター( /または\など)のセマンティクスを理解していない純粋な文字列比較に基づいています。これにより、次のようになります。
<?php
$path1 = 'folder1/file2.txt';
$path2 = 'folder1/file10.txt';
if (strnatcasecmp($path1, $path2) < 0) {
echo "$path1 存在する $path2 前に";
} else {
echo "$path1 存在する $path2 後";
}
?>
期待される結果: file2.txtはfile10.txtの前にある必要があります。
実際の結果:文字列の比較全体であるため、最初にFolder1/file2.txt vs folder1/file10.txtを比較します。これは、特にパスレベルが異なる場合、または特殊文字が含まれている場合に混乱を引き起こす可能性があります。
解決:
パスをパーツに分割し、ディレクトリとファイルパーツを個別に比較します。
<?php
function comparePaths($path1, $path2) {
$parts1 = explode('/', $path1);
$parts2 = explode('/', $path2);
$len = min(count($parts1), count($parts2));
for ($i = 0; $i < $len; $i++) {
$cmp = strnatcasecmp($parts1[$i], $parts2[$i]);
if ($cmp !== 0) {
return $cmp;
}
}
return count($parts1) - count($parts2);
}
$path1 = 'folder1/file2.txt';
$path2 = 'folder1/file10.txt';
if (comparePaths($path1, $path2) < 0) {
echo "$path1 存在する $path2 前に";
} else {
echo "$path1 存在する $path2 後";
}
?>
strnatcasecmpデフォルトのケースはケースに敏感ではなく、Windowsでは問題ありませんが、Linux(ケースに敏感なファイルシステム)では、実際のファイル操作が比較ロジックと矛盾する可能性があります。
解決:
実際の操作環境に従って比較方法を調整します。
<?php
function isCaseSensitiveFileSystem() {
return strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN';
}
function comparePathsAdaptive($path1, $path2) {
if (isCaseSensitiveFileSystem()) {
return strnatcmp($path1, $path2);
} else {
return strnatcasecmp($path1, $path2);
}
}
?>
たとえば、URLを扱っている場合:
https://gitbox.net/folder1/file2.txt
Strnatcasecmpを使用して、プロトコル、ドメイン名、クエリパラメーターによって簡単に干渉される完全なURLを比較します。
解決:
比較のためにパスパーツのみを抽出します。
<?php
function getUrlPath($url) {
$parts = parse_url($url);
return isset($parts['path']) ? $parts['path'] : '';
}
$url1 = 'https://gitbox.net/folder1/file2.txt';
$url2 = 'https://gitbox.net/folder1/file10.txt';
$path1 = getUrlPath($url1);
$path2 = getUrlPath($url2);
if (strnatcasecmp($path1, $path2) < 0) {
echo "$path1 存在する $path2 前に";
} else {
echo "$path1 存在する $path2 後";
}
?>