PHP : Détection encodage Mac Roman, UTF-8

Dans le cadre d’un projet, j’ai eu à traiter différents fichiers CSV venants de plateformes différentes (éditeur de text mac, Excel MAC 2008, Excel 2007 windows…)

Quelques recherches sur internet et questions sur divers forums m’ont permis d’arriver à un script qui semble fonctionner donc je partage des fois qu’une personne soit dans mon cas 🙂

1 – Détection d’un encodage conforme UTF-8 :

<?php
function is_utf8($string) 
{
 
	// From http://w3.org/International/questions/qa-forms-utf-8.html
	return preg_match('%^(?:
	[x09x0Ax0Dx20-x7E] # ASCII
	| [xC2-xDF][x80-xBF] # non-overlong 2-byte
	| xE0[xA0-xBF][x80-xBF] # excluding overlongs
	| [xE1-xECxEExEF][x80-xBF]{2} # straight 3-byte
	| xED[x80-x9F][x80-xBF] # excluding surrogates
	| xF0[x90-xBF][x80-xBF]{2} # planes 1-3
	| [xF1-xF3][x80-xBF]{3} # planes 4-15
	| xF4[x80-x8F][x80-xBF]{2} # plane 16
	)*$%xs', $string);
}
?>

2 – Encodage d’une chaine MacRoman en UTF-8 :

Fonction trouvée sur le site de Sébastien Guillon, pour les curieux il y fournit des explications détaillées sur les principes de cette conversion.

<?php
function MacRoman_to_utf8($str, $break_ligatures='none')
{
	// $break_ligatures : 'none' | 'fifl' | 'all'
	// 'none' : don't break any MacRoman ligatures, transform them into their utf-8 counterparts
	// 'fifl' : break only fi ("xDE" => "fi") and fl ("xDF"=>"fl")
	// 'all' : break fi, fl and also AE ("xAE"=>"AE"), ae ("xBE"=>"ae"), OE ("xCE"=>"OE") and oe ("xCF"=>"oe")

	if($break_ligatures == 'fifl')
	{
		$str = strtr($str, array("xDE"=>"fi", "xDF"=>"fl"));
	}
	
	if($break_ligatures == 'all')
	{
		$str = strtr($str, array("xDE"=>"fi", "xDF"=>"fl", "xAE"=>"AE", "xBE"=>"ae", "xCE"=>"OE", "xCF"=>"oe"));
	}

	$str = strtr($str, array("x7F"=>"x20", "x80"=>"xC3x84", "x81"=>"xC3x85",
	"x82"=>"xC3x87", "x83"=>"xC3x89", "x84"=>"xC3x91", "x85"=>"xC3x96",
	"x86"=>"xC3x9C", "x87"=>"xC3xA1", "x88"=>"xC3xA0", "x89"=>"xC3xA2",
	"x8A"=>"xC3xA4", "x8B"=>"xC3xA3", "x8C"=>"xC3xA5", "x8D"=>"xC3xA7",
	"x8E"=>"xC3xA9", "x8F"=>"xC3xA8", "x90"=>"xC3xAA", "x91"=>"xC3xAB",
	"x92"=>"xC3xAD", "x93"=>"xC3xAC", "x94"=>"xC3xAE", "x95"=>"xC3xAF",
	"x96"=>"xC3xB1", "x97"=>"xC3xB3", "x98"=>"xC3xB2", "x99"=>"xC3xB4",
	"x9A"=>"xC3xB6", "x9B"=>"xC3xB5", "x9C"=>"xC3xBA", "x9D"=>"xC3xB9",
	"x9E"=>"xC3xBB", "x9F"=>"xC3xBC", "xA0"=>"xE2x80xA0", "xA1"=>"xC2xB0",
	"xA2"=>"xC2xA2", "xA3"=>"xC2xA3", "xA4"=>"xC2xA7", "xA5"=>"xE2x80xA2",
	"xA6"=>"xC2xB6", "xA7"=>"xC3x9F", "xA8"=>"xC2xAE", "xA9"=>"xC2xA9",
	"xAA"=>"xE2x84xA2", "xAB"=>"xC2xB4", "xAC"=>"xC2xA8", "xAD"=>"xE2x89xA0",
	"xAE"=>"xC3x86", "xAF"=>"xC3x98", "xB0"=>"xE2x88x9E", "xB1"=>"xC2xB1",
	"xB2"=>"xE2x89xA4", "xB3"=>"xE2x89xA5", "xB4"=>"xC2xA5", "xB5"=>"xC2xB5",
	"xB6"=>"xE2x88x82", "xB7"=>"xE2x88x91", "xB8"=>"xE2x88x8F", "xB9"=>"xCFx80",
	"xBA"=>"xE2x88xAB", "xBB"=>"xC2xAA", "xBC"=>"xC2xBA", "xBD"=>"xCExA9",
	"xBE"=>"xE6", "xBF"=>"xC3xB8", "xC0"=>"xC2xBF", "xC1"=>"xC2xA1",
	"xC2"=>"xC2xAC", "xC3"=>"xE2x88x9A", "xC4"=>"xC6x92", "xC5"=>"xE2x89x88",
	"xC6"=>"xE2x88x86", "xC7"=>"xC2xAB", "xC8"=>"xC2xBB", "xC9"=>"xE2x80xA6",
	"xCA"=>"xC2xA0", "xCB"=>"xC3x80", "xCC"=>"xC3x83", "xCD"=>"xC3x95",
	"xCE"=>"xC5x92", "xCF"=>"xC5x93", "xD0"=>"xE2x80x93", "xD1"=>"xE2x80x94",
	"xD2"=>"xE2x80x9C", "xD3"=>"xE2x80x9D", "xD4"=>"xE2x80x98", "xD5"=>"xE2x80x99",
	"xD6"=>"xC3xB7", "xD7"=>"xE2x97x8A", "xD8"=>"xC3xBF", "xD9"=>"xC5xB8",
	"xDA"=>"xE2x81x84", "xDB"=>"xE2x82xAC", "xDC"=>"xE2x80xB9", "xDD"=>"xE2x80xBA",
	"xDE"=>"xEFxACx81", "xDF"=>"xEFxACx82", "xE0"=>"xE2x80xA1", "xE1"=>"xC2xB7",
	"xE2"=>"xE2x80x9A", "xE3"=>"xE2x80x9E", "xE4"=>"xE2x80xB0", "xE5"=>"xC3x82",
	"xE6"=>"xC3x8A", "xE7"=>"xC3x81", "xE8"=>"xC3x8B", "xE9"=>"xC3x88",
	"xEA"=>"xC3x8D", "xEB"=>"xC3x8E", "xEC"=>"xC3x8F", "xED"=>"xC3x8C",
	"xEE"=>"xC3x93", "xEF"=>"xC3x94", "xF0"=>"xEFxA3xBF", "xF1"=>"xC3x92",
	"xF2"=>"xC3x9A", "xF3"=>"xC3x9B", "xF4"=>"xC3x99", "xF5"=>"xC4xB1",
	"xF6"=>"xCBx86", "xF7"=>"xCBx9C", "xF8"=>"xC2xAF", "xF9"=>"xCBx98",
	"xFA"=>"xCBx99", "xFB"=>"xCBx9A", "xFC"=>"xC2xB8", "xFD"=>"xCBx9D",
	"xFE"=>"xCBx9B", "xFF"=>"xCBx87", "x00"=>"x20", "x01"=>"x20",
	"x02"=>"x20", "x03"=>"x20", "x04"=>"x20", "x05"=>"x20",
	"x06"=>"x20", "x07"=>"x20", "x08"=>"x20", "x0B"=>"x20",
	"x0C"=>"x20", "x0E"=>"x20", "x0F"=>"x20", "x10"=>"x20",
	"x11"=>"x20", "x12"=>"x20", "x13"=>"x20", "x14"=>"x20",
	"x15"=>"x20", "x16"=>"x20", "x17"=>"x20", "x18"=>"x20",
	"x19"=>"x20", "x1A"=>"x20", "x1B"=>"x20", "x1C"=>"x20",
	"1D"=>"x20", "x1E"=>"x20", "x1F"=>"x20", "xF0"=>""));
	
	return $str;
}

?>

3 – Compilation

Voici donc maintenant une fonction utilisant les 2 fonctions citées plus haut afin d’extraire toutes les lignes d’un fichier CSV quelque soit sont format ISO, UTF-8 ou MacRoman.
Il est certain que l’on pourra mieux faire mais à défaut, cette fonction remplie parfaitement son rôle :

<?php
function detect_encode($filename){
	if (($handle = fopen($filename, "r")) !== FALSE) {
		$encodage="";
		$buffer="";
		$return=array();
		
		while (($buffer = fgets($handle, 1000)) !== false) {
			//On detecte si c'est de l'UTF-8
			$encodage =mb_detect_encoding($buffer,'auto',true);
			if($encodage!="UTF-8"){
				$buffer2=htmlentities($buffer);
				if(is_utf8($buffer2)==0){
					//MacRoman
					$buffer=MacRoman_to_utf8($buffer);
					//Il faut détecter toutes les lignes car la lecture d'un CSV Mac se retrouve sur une seule ligne...
					$buffer = nl2br($buffer);
					$e_buffer=explode("<br />",$buffer);
					if(count($e_buffer)>0){
						foreach($e_buffer as $b){
							array_push($return,$b);
						}
					}else{
						array_push($return,$buffer);
					}
				}else{
					//Autre
					$buffer=utf8_encode($buffer);
					array_push($return,$buffer);
				}
			}else{
				//UTF-8
				array_push($return,$buffer);
			}
		}
		fclose($handle);	
	}
	return $return;
}
?>

Voilà avant tout un aide mémoire pour moi, et peut-être une aide pour vous 😉
Et je le répète, si vous avez une solution plus simple, je suis ouvert !

Sinon il y a aussi...