ホームページ制作/SEO対策なら株式会社サイバーブレーンへ

 03-5961-5681 平日10:00~19:00

 2017-11-21

グレイヴ・アクセントが勝手にはずされる!?ファイル名の多言語についてメモ

認識できないファイル名 - windows10のコマンドプロンプト

グレイヴ・アクセント(caffè.png)を使ったファイル名が処理できなかった話であるが、ファイル名が読めないわけではなく、あるディレクトリー配下のファイル名一覧を取得すると、グレイヴ・アクセントが外されたアルファベットに変換されてしまう事である。これには正直、驚きました。
何故かというと、OS上では問題なく処理出来ているファイル名がコマンドでPHPプログラムを実行すると処理できないという事が起きるとは思っていなかったからです。
(もちろんchcpコマンドで文字コードをUTF-8にしてから実行しています。)

ソース

C:¥work>php -f renameAll.php -- -d img -s ".png" -r ".jpg"
imgディレクトリーに該当のファイルがあり、その他のファイルも含めて拡張子のpngをjpgにファイル名変換しようとしているのが、上記のサンプルです。
PHPスクリプトのソースは以下になります。

<?php
mb_internal_encoding('UTF-8');

$shortopts ='d:';//ディレクトリー名の指定
$shortopts.='s:';//検索対象のファイル名部分一致文字列
$shortopts.='r:';//ファイル名部分一致した個所の置換文字列

$longopts  = array(
    "real",//テストモードではなく実施に実行する。
    "recursive"//ディレクトリーを再起検索をする場合に指定する。
);

$options = getopt($shortopts,$longopts);
//var_dump($options);

$G_RunFileReplace=false;//ファイル名変更を実行するかどうかのフラグ
$G_RunFileReplaceTest=true;//テストモードで実行する場合のフラグー実際にファイル名変更をせずに確認するため。
$G_Recursive=false;//再起検索フラグ -- 今回は使わない

$G_Search=$G_Replace='';

$dir ='.';

//引数からの各種設定
if (isset($options['d']) && is_string($options['d']) && $options['d']!=''){//この3条件必須
	$dir=$options['d'];
}
if (isset($options['s']) && is_string($options['s']) && $options['s']!=''){
	$G_Search=$options['s'];
	
	if (isset($options['r']) && is_string($options['r'])){//空も許可する。
		$G_Replace=$options['r'];
		$G_RunFileReplace=true;
	}
}
if (isset($options['real'])){
	$G_RunFileReplaceTest=false;
}
if (isset($options['recursive'])){
	$G_Recursive=true;
}



if ($G_RunFileReplace){
	$dir = rtrim($dir, DIRECTORY_SEPARATOR);
	fileNameReplace($dir);
} else {
	$fileList = scandir($dir);
	var_dump($fileList);
}

/*
 * ファイル名の変更
 *
 */
function fileNameReplace($dir){
	global $G_Search, $G_Replace, $G_RunFileReplaceTest,$G_Recursive;
	
	if(!isset($G_Search) || !is_string($G_Search) || $G_Search==''){
		return false;
	}
	if(!isset($G_Replace) || !is_string($G_Replace)){
		return false;
	}
	if(!isset($G_RunFileReplaceTest) || !is_bool($G_RunFileReplaceTest)){
		return false;
	}
	if(!isset($G_Recursive) || !is_bool($G_Recursive)){
		return false;
	}
	
	return rvFileNameReplace($dir);
}

function rvFileNameReplace($dir){
	global $G_Search, $G_Replace, $G_RunFileReplaceTest,$G_Recursive;
	
	$dh = @opendir($dir);
	if (!$dh) {
		outright($dir . ' を開けません'.PHP_EOL, true);
		return false;
	}
	while (false !== ($name = readdir($dh))) {
		// 自分自身と親ディレクトリを除外
		if ($name == '.' || $name == '..') {
			continue;
		}
		//あり得ないと思うが、空のなまえを除外
		if ($name == ''){
			outright($dir . ' には空のファイル名が存在します'.PHP_EOL, true);
			continue;
		}
		
		$path = $dir.DIRECTORY_SEPARATOR.$name;
		$type = @filetype($path);
		if ($type===false){
			outright('Read Error: '.$name . PHP_EOL.' --> ファイル名に認識できない文字が含まれている可能性があります'.PHP_EOL);
			//hexByteOut($name);
			continue;
		}
		
		switch ($type) {
			case 'file'://通常ファイルの場合
				if (strpos($name, $G_Search)===false){//マッチングしなければスキップ
					break;
				}
				$newName = str_replace($G_Search, $G_Replace, $name);
				if ($newName==''){//結果のファイル名が空なら変更しない
					break;
				}
				if (!file_exists($path)){//ファイルが存在しない
					break;
				}
				
				$newPath = $dir.DIRECTORY_SEPARATOR.$newName;
				if (file_exists($newPath)){//同名のファイルが存在する
					outright('No Change: '.$name . PHP_EOL.' --> '.$newName.' が存在するため変更できません'.PHP_EOL);
				} else {
					if ($G_RunFileReplaceTest){
						outright('Test Change: '.$name . PHP_EOL.' --> '.$newName.PHP_EOL);
					} else {
						if (!@rename($path, $newPath)){
							outright('No Change: '.$name . PHP_EOL.' --> '.$newName.' への変更で失敗しました'.PHP_EOL);
						}
					}
				}
				break;
			case 'dir':// ディレクトリは自分自身を呼出す
				if ($G_Recursive) {
					
				}
				break;
			default:
				//echo "INFO: $path$file is $type\n";
				break;
		}
	}
	closedir($dh);
	
	return true;
}

function outright($str,$err=false){
	if (php_sapi_name() != 'cli') {
		return false;
	}
	if ($err){
		$stream='php://stderr';
	} else {
		$stream='php://stdout';
	}
	$stdout = fopen($stream, 'w');
	fwrite($stdout, $str);
	fclose($stdout);
}

function hexByteOut($str,$width=16){

	for($i=0,$w=0; $i < strlen($str); $i++) {
		echo '0x'.bin2hex(substr($str, $i, 1)).' ';
		
		$w++;
		if ($w==$width){
			echo PHP_EOL;
			$w=0;
		}
	}
}

LINUX - CentOS 6 で試す。

しかし、同じことをLinuxで試すと全く問題が発生しません。
しかもターミナルの文字コード設定はEUC-JPですが、そのままで全く問題ありません。
------------------------------------------------------------------------------------------
[xxxx@svhost wk]$ php -f renameAll.php -- -d img -s ".png" -r ".jpg"
Test Change: badge.png
--> badge.jpg
Test Change: caffè.png
--> caffè.jpg
Test Change: icon.png
--> icon.jpg
------------------------------------------------------------------------------------------

改めて、Windowsのコマンドプロンプトはおまけ程度にしか利用できないのか?と嘆いているところです。
今回は、仕方なく手動で該当ファイルの名前を Windows10のExplorer(エクスプローラ)から変更しました。

~この記事の著者~

文字コードのトラブル

 03-5961-5681 平日10:00~19:00

メールフォーム

あなたが
サイトに
求める要素
あなたがサイトに求める0要素