String in Float umwandeln (Thema: PHP Beispiele)

Konvertierung von String zu Float in PHP

1. Einleitung

Die Umwandlung von String zu Float ist in PHP nicht trivial. Grundproblem dabei ist, dass der String in der Regel nicht nur Zahlen von 0 bis 9 enthält, sondern mindestens auch das Trennzeichen für die Nachkommastellen und eventuell auch Kennzeichnungen für die Tausenderstellen. Diese Zeichen wiederum unterscheiden sich je nach Region. In den USA werden Tausenderstellen mit Kommata markiert und Nackommastellen mit einem Punkt vom Rest getrennt. In Deutschland sind die Zeichen genau umgekehrt: Punkt für Tausenderstellen und Komma am Anfang des Nachkommabereichs. Es gibt keine Funktion in PHP, die mit beiden Schreibweisen zurechtkommt. Das häufig verwendete floatval($str), welches $str in einen Float umwandelt, scheitert bereits, wenn der String neben dem Punkt auch Kommata nach US-amerikanischer Schreibweise (also für Tausenderstellen) enthält. Noch schlechter ist das Ergebnis bei der europäischen Schreibweise, da floatval hier kurzerhand alles nach einem Komma ignoriert und den ersten Punkt als Zeichen für den Beginn des Nachkommabereichs interpretiert. In den nachfolgenden Beispielen soll daher eine eigene Funktion entwickelt werden, welche die Schwächen von floatval() behebt.

2. Beispiel und Auswirkungen der locale-Einstellung

In diesem Beispiel wird zunächst floatval() vorgeführt. Dabei werden auch passende locale-Einstellungen getroffen, um auszuschließen, dass floatval() eventuell in Abhängigkeit von diesen den String auswertet. Wie sich herausstellt ist dies tatsächlich nicht der Fall. floatval() schlägt konsequent fehl, sobald ein Komma verwendet wird.

PHP-Code
<?php
	setlocale(LC_ALL, 'de_DE@euro', 'de_DE', 'de', 'ge', 'deu', 'germany');
	var_dump(setlocale(LC_ALL, 0)); // gibt die aktuelle locale-Einstellung aus
	echo '123,123.123 => ' . floatval('123,123.123') . "\n";
	echo '123.123,123 => ' . floatval('123.123,123') . "\n";
	echo '123123.123 => '  . floatval('123123.123') . "\n";
	echo '123123,123 => '  . floatval('123123,123') . "\n";
	
	setlocale(LC_ALL, 'en_US', 'en_EN', 'en', 'us', 'usa', 'united states');
	var_dump(setlocale(LC_ALL, 0)); // gibt die aktuelle locale-Einstellung aus
	echo '123,123.123 => ' . floatval('123,123.123') . "\n";
	echo '123.123,123 => ' . floatval('123.123,123') . "\n";
	echo '123123.123 => '  . floatval('123123.123') . "\n";
	echo '123123,123 => '  . floatval('123123,123') . "\n";
?>

HTML-Code: Ausgabe
string(19) "German_Germany.1252"
123,123.123 => 123
123.123,123 => 123,123
123123.123 => 123123,123
123123,123 => 123123
string(26) "English_United States.1252"
123,123.123 => 123
123.123,123 => 123.123
123123.123 => 123123.123
123123,123 => 123123


3. Eine eigene flotval-Funktion

In diesem Beispiel wird eine erste eigene Funktion zum Umwandeln von String nach Float definiert. Die Funktion strToFloat($str) prüft zunächst, ob der übergebene Parameter überhaupt ein String ist. Falls nicht wird eine Fehlermeldung geworfen. (Es sei denn es handelt sich um Integer oder Float, die nach Anwendung von floatval() zurückgegeben werden.) Danach werden mit trim() eventuelle Leerzeichen (und ähnliches) am Anfang und Ende des Strings entfernt. Es wird mit einem regulärem Ausdruck geprüft, ob sich der String theoretisch in einen Float umwandeln lässt. Die Prüfung ist sehr grob und läuft nach dem Muster „darf mit einem Vorzeichen starten, muss dann eine Ziffer (0-9) haben, darf dann beliebig viele Ziffern, Kommata oder Punkte haben”. Entsprechend würden hier auch manche Strings durchrutschen, die eigentlich keine gültigen Zahlen sind (zum Beispiel „0,98.233....23”). Um diese Ungenauigkeit zu umgehen müsste der reguläre Ausdruck allerdings deutlich komplizierter sein.

Im Anschluss an diese Prüfung wird nach dem letzten Komma oder Punkt im String gesucht. Alle Kommata oder Punkte davor werden entfernt und das Zeichen selbst wird immer durch einen Punkt ersetzt. So hat die Zahl am Ende eine Form der Art „-1234.1234”, also immer mit maximal einem Punkt und optional mit Vorzeichen. Diese Zahl kann dann von PHP über floatval() in automatisch umgewandelt werden.

PHP-Code
<?php
	function strToFloat($str) {
		if (is_int($str) || is_float($str)) { return floatval($str); }
		if (!is_string($str)) { throw new Exception('String expected but received '.gettype($str).'.'); }
		$str = trim($str);
		if (!preg_match('/^(\-|\+)?[0-9][0-9\,\.]*/', $str)) { throw new Exception("Could not convert string to float. Given string does not match expected number format."); }

		$last = max(strrpos($str, ','), strrpos($str, '.'));
		if ($last!==false) {
			$str = strtr($str, ',.', 'XX');
			$str[$last] = '.';
			$str = str_replace('X', '', $str); // strtr funktioniert nicht mit $to=''
		}
		return (float)$str;
	}
	
	var_dump(strToFloat('123'));
	var_dump(strToFloat('123.152'));
	var_dump(strToFloat('-353,344,345.12'));
	var_dump(strToFloat('134.332.153,323'));
	var_dump(strToFloat('2434,,223'));
	var_dump(strToFloat('13412.'));
	
	try { var_dump(strToFloat(true)); } catch (Exception $e) { echo $e->getMessage()."\n"; }
	try { var_dump(strToFloat('hotdog')); } catch (Exception $e) { echo $e->getMessage(); }
?>

HTML-Code: Ausgabe
float(123)
float(123.152)
float(-353344345.12)
float(134332153.323)
float(2434.223)
float(13412)
String expected but received boolean.
Could not convert string to float. Given string does not match expected number format.

4. Beispiel 2

In diesem Beispiel wird die vorherige Funktion etwas gekürzt, sodass sie kompakter ist. Diesmal gibt sie keine Fehlermeldungen zurück, falls ungültige Datentypen übergeben wurden und es findet keine genaue Prüfung des Aufbaus des Strings statt. Stattdessen werden pauschal alle Zeichen entfernt, die kein Vorzeichen, kein Komma, kein Punkt und keine Ziffer (0-9) sind. Danach werden sämtliche Kommata durch Punkte ersetzt und die Position des letzten Punktes wird gespeichert. Wurde mindestens ein Punkt gefunden, dann werden alle Punkte vor dessen Position entfernt und es wird floatval() auf das Ergebnis angewendet. Andernfalls wird an floatval() einfach der gesamte String übergeben (da er keine Punkte/Kommata enthält).

PHP-Code
<?php
	function strToFloat($str) {
		$str = preg_replace('[^0-9\,\.\-\+]', '', strval($str));
		$str = strtr($str, ',', '.');
		$pos = strrpos($str, '.');
		return ($pos===false ? floatval($str) : floatval(str_replace('.', '', substr($str, 0, $pos)) . substr($str, $pos)));
	}
	
	echo '123 => ' . strToFloat('123') . "\n";
	echo '123.152 => ' . strToFloat('123.152') . "\n";
	echo '-353,344,345.12 => ' . strToFloat('-353,344,345.12') . "\n";
	echo '134.332.153,323 => ' . strToFloat('134.332.153,323') . "\n";
	echo '2434,,223 => ' . strToFloat('2434,,223') . "\n";
	echo '13412. => ' . strToFloat('13412.') . "\n";
	echo 'true => ' . strToFloat(true) . "\n";
	echo 'hotdog => ' . strToFloat('hotdog') . "\n";
?>

HTML-Code: Ausgabe
123 => 123
123.152 => 123.152
-353,344,345.12 => -353344345.12
134.332.153,323 => 134332153.323
2434,,223 => 2434.223
13412. => 13412
true => 1
hotdog => 0


5. Beispiel 3

Die vorherige Funktion wird hier noch mal gekürzt, sodass sie nur noch aus zwei Zeilen besteht. Das Entfernen aller ungültigen Zeichen erfolgt diesmal nicht, könnte aber theoretisch in den zwei Zeilen noch untergebracht werden. Ansonsten ist das Verhalten letztlich identisch: Kommata durch Punkte ersetzen, letzten Punkt suchen, gebenenfalls alle Punkte vor diesem Entfernen und floatval() anwenden.

PHP-Code
<?php
	function strToFloat($str) {
		$pos = strrpos($str = strtr(trim(strval($str)), ',', '.'), '.');
		return ($pos===false ? floatval($str) : floatval(str_replace('.', '', substr($str, 0, $pos)) . substr($str, $pos)));
	}
	
	echo '123 => ' . strToFloat('123') . "\n";
	echo '123.152 => ' . strToFloat('123.152') . "\n";
	echo '-353,344,345.12 => ' . strToFloat('-353,344,345.12') . "\n";
	echo '134.332.153,323 => ' . strToFloat('134.332.153,323') . "\n";
	echo '2434,,223 => ' . strToFloat('2434,,223') . "\n";
	echo '13412. => ' . strToFloat('13412.') . "\n";
	echo 'true => ' . strToFloat(true) . "\n";
	echo 'hotdog => ' . strToFloat('hotdog') . "\n";
?>

HTML-Code: Ausgabe
123 => 123
123.152 => 123.152
-353,344,345.12 => -353344345.12
134.332.153,323 => 134332153.323
2434,,223 => 2434.223
13412. => 13412
true => 1
hotdog => 0


Kommentare (1)

Von neu nach alt
Danke für das Dingens, aber muss das nicht korrekt:

$str = preg_replace('[^0-9\,\.\-\+]', '', strval($str));

heißen. Wie oben hat es bei mir nicht funktioniert.
tpk (Gast) #