I have to read config files in PHP which contain entries with semicolons, e.g.
[section]
key=value;othervalue
I notices that parse_ini_file() removes all semicolons and what follows, even when set to INI_SCANNER_RAW.
The INI files come from legacy systems and I can't change the format. I only have to read them.
What's the best tool to use when I have to preserve the entries with semicolons?
I would recommend reading the file into an array first, convert the semicolons to pipes |, then spit that out to a temporary file and use parse_ini_file() with that new temporary file.
Like so...
$string = file_get_contents('your_file');
$newstring = str_replace(";","|",$string);
$tempfile = 'your_temp_filename';
file_put_contents($tempfile, $newstring);
$arrIni = parse_ini_file($tempfile);
Then after that you could always replace the pipes with semicolons as you enumerate your new INI based array.
For ini files, the ; is the comment symbol.
So it's actually a good idea to not use it for something else.
However, you can use this slightly modified functions from the solutions found here :
<?php
//Credits to goulven.ch AT gmail DOT com
function parse_ini ( $filepath )
{
$ini = file( $filepath );
if ( count( $ini ) == 0 ) { return array(); }
$sections = array();
$values = array();
$globals = array();
$i = 0;
foreach( $ini as $line ){
$line = trim( $line );
// Comments
if ( $line == '' || $line{0} == ';' ) { continue; }
// Sections
if ( $line{0} == '[' )
{
$sections[] = substr( $line, 1, -1 );
$i++;
continue;
}
// Key-value pair
list( $key, $value ) = explode( '=', $line, 2 );
$key = trim( $key );
$value = trim( $value );
if (strpos($value, ";") !== false)
$value = explode(";", $value);
if ( $i == 0 ) {
// Array values
if ( substr( $line, -1, 2 ) == '[]' ) {
$globals[ $key ][] = $value;
} else {
$globals[ $key ] = $value;
}
} else {
// Array values
if ( substr( $line, -1, 2 ) == '[]' ) {
$values[ $i - 1 ][ $key ][] = $value;
} else {
$values[ $i - 1 ][ $key ] = $value;
}
}
}
for( $j=0; $j<$i; $j++ ) {
$result[ $sections[ $j ] ] = $values[ $j ];
}
return $result + $globals;
}
You can see examples of usage following the link.
Related
I have an array of words. e.g.,
$pattern = ['in', 'do', 'mie', 'indo'];
I wanna split a words match by the patterns to some ways.
input =
indomie
to output =
$ in, do, mie
$ indo, mie
any suggests?
*ps sorry for bad english. thank you very much!
it was an very interesting question.
Input:-
$inputSting = "indomie";
$pattern = ['in', 'do', 'mie', 'indo','dom','ie','indomi','e'];
Output:-
in,do,mie
in,dom,ie
indo,mie
indomi,e
Approach to this challenge
Get the pattern string length
Get the all the possible combination of matrix
Check whether the pattern matching.
if i understand your question correctly then above #V. Prince answer will work only for finding max two pattern.
function sampling($chars, $size, $combinations = array()) {
if (empty($combinations)) {
$combinations = $chars;
}
if ($size == 1) {
return $combinations;
}
$new_combinations = array();
foreach ($combinations as $combination) {
foreach ($chars as $char) {
$new_combinations[] = $combination . $char;
}
}
return sampling($chars, $size - 1, $new_combinations);
}
function splitbyPattern($inputSting, $pattern)
{
$patternLength= array();
// Get the each pattern string Length
foreach ($pattern as $length) {
if (!in_array(strlen($length), $patternLength))
{
array_push($patternLength,strlen($length));
}
}
// Get all the matrix combination of pattern string length to check the probablbe match
$combination = sampling($patternLength, count($patternLength));
$MatchOutput=Array();
foreach ($combination as $comp) {
$intlen=0;
$MatchNotfound = true;
$value="";
// Loop Through the each probable combination
foreach (str_split($comp,1) as $length) {
if($intlen<=strlen($inputSting))
{
// Check whether the pattern existing
if(in_array(substr($inputSting,$intlen,$length),$pattern))
{
$value = $value.substr($inputSting,$intlen,$length).',';
}
else
{
$MatchNotfound = false;
break;
}
}
else
{
break;
}
$intlen = $intlen+$length;
}
if($MatchNotfound)
{
array_push($MatchOutput,substr($value,0,strlen($value)-1));
}
}
return array_unique($MatchOutput);
}
$inputSting = "indomie";
$pattern = ['in', 'do', 'mie', 'indo','dom','ie','indomi','e'];
$output = splitbyPattern($inputSting,$pattern);
foreach($output as $out)
{
echo $out."<br>";
}
?>
try this one..
and if this solves your concern. try to understand it.
Goodluck.
<?php
function splitString( $pattern, $string ){
$finalResult = $semiResult = $output = array();
$cnt = 0;
# first loop of patterns
foreach( $pattern as $key => $value ){
$cnt++;
if( strpos( $string, $value ) !== false ){
if( implode("",$output) != $string ){
$output[] = $value;
if( $cnt == count($pattern) ) $semiResult[] = implode( ",", $output );
}else{
$semiResult[] = implode( ",", $output );
$output = array();
$output[] = $value;
if( implode("",$output) != $string ){
$semiResult[] = implode( ",", $output );
}
}
}
}
# second loop of patterns
foreach( $semiResult as $key => $value ){
$stackString = explode(",", $value);
/* if value is not yet equal to given string loop the pattern again */
if( str_replace(",", "", $value) != $string ){
foreach( $pattern as $key => $value ){
if( !strpos(' '.implode("", $stackString), $value) ){
$stackString[] = $value;
}
}
if( implode("", $stackString) == $string ) $finalResult[] = implode(",", $stackString); # if result equal to given string
}else{
$finalResult[] = $value; # if value is already equal to given string
}
}
return $finalResult;
}
$pattern = array('in','do','mie','indo','mi','e', 'i');
$string = 'indomie';
var_dump( '<pre>',splitString( $pattern, $string ) );
?>
I have case by equating the existing data
i Have 2 data with format like this
$data1 = "144|aku|1!!!122|dia|2"; data description "id|name|amount"
$data2 = "144|aku|1!!!211|dia|1";
there is the same data in 2 arrays, how to make the same data in the amount section can be added and unequal data will be inserted into the array
how to make the 2 arrays become like this
$data_result = "144|aku|2!!!122|dia|2!!!211|dia|1";
this my full script
$data1 = "144|aku|1!!!122|dia|2";
$data2 = "144|aku|1!!!211|dia|1";
$simpan_array = array();
$array_status = array();
// Pecah Array dari user dulu
$array_code_user = explode('!!!', $data1 );
$jumlah_item_user = count($array_code_user) - 1;
$x = 0;
// Pecah Array dari lokal
$array_cart_local = explode('!!!',$data2 );
$jumlah_cart_local = count($array_cart_local) - 1;
$j = 0;
while( $x <= $jumlah_item_user ) {
$ambil_datacart_user = explode( '|', $array_code_user[$x] );
$idproduk_user = $ambil_datacart_user[0];
$namaprod_user = $ambil_datacart_user[1];
$jumprod_user = $ambil_datacart_user[2];
$simpan_array_0 = $idproduk_user.'|'.$namaprod_user.'|'.$jumprod_user;
while( $j <= $jumlah_cart_local ) {
$ambil_datacart_lokal = explode( '|', $array_cart_local[$j] );
$idprod_lokal = $ambil_datacart_lokal[0];
$namaprod_lokal = $ambil_datacart_lokal[1];
$jumprod_lokal = $ambil_datacart_lokal[2];
$simpan_array_1 = $idprod_lokal.'|'.$namaprod_lokal.'|'.$jumprod_lokal;
//Disamakan
if( $idproduk_user == $idprod_lokal ) {
$jumtotal = $jumprod_user + $jumprod_lokal;
$simpan_array[] = $idprod_lokal.'|'.$namaprod_lokal.'|'.$jumtotal;
$array_status[] = 1;
} else {
$simpan_array[] = $simpan_array_1;
$array_status[] = 0;
}
$j++;
}
$x++;
}
$jumlah = array_sum($array_status);
$array_jadi = join("!!!",$simpan_array);
//$datakembali = $array_jadi;
if ( $jumlah == 0 ) {
$datakembali = $simpan_array_1.'!!!'.$array_jadi;
} else {
$datakembali = $array_jadi;
}
echo $datakembali;
?>
I loop the first array and call the output "result".
Then as I loop the second I see if the data exists in result array and it they do I add it to result else I create the new item.
$array1 = explode("!!!", $array1);
$array2 = explode("!!!", $array2);
Foreach($array1 as $val){
List($id, $name, $amount) = explode("|", $val);
$result[$id] = ['name' => $name, 'amount' => $amount];
}
Foreach($array2 as $val){
List($id, $name, $amount) = explode("|", $val);
If(isset($result[$id])){
$result[$id]['amount'] += $amount;
}Else{
$result[$id] = ['name' => $name, 'amount' => $amount];
}
}
https://3v4l.org/gQjTj
I did not include the conversion back to your format.
It's a cute format with all the pipes and exclamation marks but I strongly encourage you to use json_encode().
That makes a string that can easily be decoded again without home made functions.
See here https://3v4l.org/IEhsp
You can do something like:
$array1 = "144|aku|1!!!122|dia|2";
$array2 = "144|aku|1!!!211|dia|1";
//Convert the string into array by explode
$array1 = explode( '!!!', $array1 );
$array2 = explode( '!!!', $array2 );
//Merge the 2 arrays
$merged = array_merge($array1,$array2);
//Group the array using foreach
//Loop thru the array and group using the id as key
$grouped = array();
foreach( $merged as $key => $val ) {
$val = explode( '|', $val ); //Convert the string into array
if ( !isset( $grouped[ $val[0] ] ) ) $grouped[ $val[0] ] = $val; //If key does not exist, initialise the data
else $grouped[ $val[0] ][2] += $val[2]; //If key already exist, just add the third value
}
//Format the grouped array into string
foreach( $grouped as $key => $val ) {
$grouped[ $key ] = implode( '|', $val );
}
$result = implode( '!!!', $grouped );
echo $result;
This will result to:
144|aku|2!!!122|dia|2!!!211|dia|1
I have a number of DBF database files that I would like to convert to CSVs. Is there a way to do this in Linux, or in PHP?
I've found a few methods to convert DBFs, but they are very slow.
Try soffice (LibreOffice):
$ soffice --headless --convert-to csv FILETOCONVERT.DBF
Change the files variable to a path to your DBF files. Make sure the file extension matches the case of your files.
set_time_limit( 24192000 );
ini_set( 'memory_limit', '-1' );
$files = glob( '/path/to/*.DBF' );
foreach( $files as $file )
{
echo "Processing: $file\n";
$fileParts = explode( '/', $file );
$endPart = $fileParts[key( array_slice( $fileParts, -1, 1, true ) )];
$csvFile = preg_replace( '~\.[a-z]+$~i', '.csv', $endPart );
if( !$dbf = dbase_open( $file, 0 ) ) die( "Could not connect to: $file" );
$num_rec = dbase_numrecords( $dbf );
$num_fields = dbase_numfields( $dbf );
$fields = array();
$out = '';
for( $i = 1; $i <= $num_rec; $i++ )
{
$row = #dbase_get_record_with_names( $dbf, $i );
$firstKey = key( array_slice( $row, 0, 1, true ) );
foreach( $row as $key => $val )
{
if( $key == 'deleted' ) continue;
if( $firstKey != $key ) $out .= ';';
$out .= trim( $val );
}
$out .= "\n";
}
file_put_contents( $csvFile, $out );
}
Using #Kohjah's code, here an update of the code using a better (IMHO) fputcsv approach:
// needs dbase php extension (http://php.net/manual/en/book.dbase.php)
function dbfToCsv($file)
{
$output_path = 'output' . DIRECTORY_SEPARATOR . 'path';
$path_parts = pathinfo($file);
$csvFile = path_parts['filename'] . '.csv';
$output_path_file = $output_path . DIRECTORY_SEPARATOR . $csvFile;
if (!$dbf = dbase_open( $file, 0 )) {
return false;
}
$num_rec = dbase_numrecords( $dbf );
$fp = fopen($output_path_file, 'w');
for( $i = 1; $i <= $num_rec; $i++ ) {
$row = dbase_get_record_with_names( $dbf, $i );
if ($i == 1) {
//print header
fputcsv($fp, array_keys($row));
}
fputcsv($fp, $row);
}
fclose($fp);
}
I making a plugin for wordpress, but i have problem with allowed memory size on my server, it is 128, they dont allow me to increase memory at run time.
my plugin have function to export user datas to csv and email to user. i getting fetal error on this wordpress, functions.php line 252
is there efficient way to optimize this below function to prevent getting error
thank you
function is_serialized( $data ) {
// if it isn't a string, it isn't serialized
if ( ! is_string( $data ) )
return false;
$data = trim( $data );
if ( 'N;' == $data )
return true;
$length = strlen( $data );
if ( $length < 4 )
return false;
if ( ':' !== $data[1] )
return false;
$lastc = $data[$length-1];
if ( ';' !== $lastc && '}' !== $lastc )
return false;
$token = $data[0];
switch ( $token ) {
case 's' :
if ( '"' !== $data[$length-2] )
return false;
case 'a' :
case 'O' :
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
case 'b' :
case 'i' :
case 'd' :
return (bool) preg_match( "/^{$token}:[0-9.E-]+;\$/", $data );
}
return false;
}
my function - fileds are dynamic getting from admin panel
$outFile = '';
$blogusers = get_users();
// retrieve the current options
$spueIntervall = get_option('spue_intervall');
//fileds are dynamic
$spueSeperator = get_option('spue_seperator');
$spueFields = get_option('spue_fields');
// Check if the functions is already loaded
if (!function_exists('get_userdata'))
require_once (ABSPATH . 'wp-includes/pluggable.php');
// Setup the top-line of the file
foreach ($spueFields AS $fieldKey => $fieldValue)
{
if ($fieldValue == 1)
{
$outFile .= $fieldKey . $spueSeperator;
}
}
$outFile .= "\n";
// Loop to all users
foreach ($blogusers as $user)
{
// Get the userdate
$user_info = get_userdata($user->ID);
// Only output the needed data
foreach ($spueFields AS $fieldKey => $fieldValue)
{
if ($fieldValue == 1)
{
$outFile .= '"' . $user_info->{$fieldKey} . '"' . $spueSeperator;
}
}
$outFile .= "\n";
}
// Save
file_put_contents( dirname(__FILE__) . '\spue-export.csv', utf8_encode($outFile));
got it
foreach ( $blogusers as $user ) {
$data = array();
foreach ($spueFields AS $fieldKey => $fieldValue) {
if ($fieldValue == 1)
{
$value = isset( $user->{$fieldKey} ) ? $user->{$fieldKey} : '';
$value = is_array( $value ) ? serialize( $value ) : $value;
$data[] = '"' . str_replace( '"', '""', $value ) . '"';
$outFile.=implode( ',', $data ) . "\n";
}
It was hinted in a comment to an answer to this question that PHP can not reverse Unicode strings.
As for Unicode, it works in PHP
because most apps process it as
binary. Yes, PHP is 8-bit clean. Try
the equivalent of this in PHP: perl
-Mutf8 -e 'print scalar reverse("ほげほげ")' You will get garbage,
not "げほげほ". – jrockway
And unfortunately it is correct that PHPs unicode support atm is at best "lacking". This will hopefully change drastically with PHP6.
PHPs MultiByte functions does provide the basic functionality you need to deal with unicode, but it is inconsistent and does lack a lot of functions. One of these is a function to reverse a string.
I of course wanted to reverse this text for no other reason then to figure out if it was possible. And I made a function to accomplish this enormous complex task of reversing this Unicode text, so you can relax a bit longer until PHP6.
Test code:
$enc = 'UTF-8';
$text = "ほげほげ";
$defaultEnc = mb_internal_encoding();
echo "Showing results with encoding $defaultEnc.\n\n";
$revNormal = strrev($text);
$revInt = mb_strrev($text);
$revEnc = mb_strrev($text, $enc);
echo "Original text is: $text .\n";
echo "Normal strrev output: " . $revNormal . ".\n";
echo "mb_strrev without encoding output: $revInt.\n";
echo "mb_strrev with encoding $enc output: $revEnc.\n";
if (mb_internal_encoding($enc)) {
echo "\nSetting internal encoding to $enc from $defaultEnc.\n\n";
$revNormal = strrev($text);
$revInt = mb_strrev($text);
$revEnc = mb_strrev($text, $enc);
echo "Original text is: $text .\n";
echo "Normal strrev output: " . $revNormal . ".\n";
echo "mb_strrev without encoding output: $revInt.\n";
echo "mb_strrev with encoding $enc output: $revEnc.\n";
} else {
echo "\nCould not set internal encoding to $enc!\n";
}
here's another approach using regex:
function utf8_strrev($str){
preg_match_all('/./us', $str, $ar);
return implode(array_reverse($ar[0]));
}
Here's another way. This seems to work without having to specify an output encoding (tested with a couple of different mb_internal_encodings):
function mb_strrev($text)
{
return join('', array_reverse(
preg_split('~~u', $text, -1, PREG_SPLIT_NO_EMPTY)
));
}
Grapheme functions handle UTF-8 string more correctly than mbstring and PCRE functions/ Mbstring and PCRE may break characters. You can see the defference between them by executing the following code.
function str_to_array($string)
{
$length = grapheme_strlen($string);
$ret = [];
for ($i = 0; $i < $length; $i += 1) {
$ret[] = grapheme_substr($string, $i, 1);
}
return $ret;
}
function str_to_array2($string)
{
$length = mb_strlen($string, "UTF-8");
$ret = [];
for ($i = 0; $i < $length; $i += 1) {
$ret[] = mb_substr($string, $i, 1, "UTF-8");
}
return $ret;
}
function str_to_array3($string)
{
return preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
}
function utf8_strrev($string)
{
return implode(array_reverse(str_to_array($string)));
}
function utf8_strrev2($string)
{
return implode(array_reverse(str_to_array2($string)));
}
function utf8_strrev3($string)
{
return implode(array_reverse(str_to_array3($string)));
}
// http://www.php.net/manual/en/function.grapheme-strlen.php
$string = "a\xCC\x8A" // 'LATIN SMALL LETTER A WITH RING ABOVE' (U+00E5)
."o\xCC\x88"; // 'LATIN SMALL LETTER O WITH DIAERESIS' (U+00F6)
var_dump(array_map(function($elem) { return strtoupper(bin2hex($elem)); },
[
'should be' => "o\xCC\x88"."a\xCC\x8A",
'grapheme' => utf8_strrev($string),
'mbstring' => utf8_strrev2($string),
'pcre' => utf8_strrev3($string)
]));
The result is here.
array(4) {
["should be"]=>
string(12) "6FCC8861CC8A"
["grapheme"]=>
string(12) "6FCC8861CC8A"
["mbstring"]=>
string(12) "CC886FCC8A61"
["pcre"]=>
string(12) "CC886FCC8A61"
}
IntlBreakIterator can be used since PHP 5.5 (intl 3.0);
function utf8_strrev($str)
{
$it = IntlBreakIterator::createCodePointInstance();
$it->setText($str);
$ret = '';
$pos = 0;
$prev = 0;
foreach ($it as $pos) {
$ret = substr($str, $prev, $pos - $prev) . $ret;
$prev = $pos;
}
return $ret;
}
The answer
function mb_strrev($text, $encoding = null)
{
$funcParams = array($text);
if ($encoding !== null)
$funcParams[] = $encoding;
$length = call_user_func_array('mb_strlen', $funcParams);
$output = '';
$funcParams = array($text, $length, 1);
if ($encoding !== null)
$funcParams[] = $encoding;
while ($funcParams[1]--) {
$output .= call_user_func_array('mb_substr', $funcParams);
}
return $output;
}
Another method:
function mb_strrev($str, $enc = null) {
if(is_null($enc)) $enc = mb_internal_encoding();
$str = mb_convert_encoding($str, 'UTF-16BE', $enc);
return mb_convert_encoding(strrev($str), $enc, 'UTF-16LE');
}
It is easy utf8_strrev( $str ). See the relevant source code of my Library that I copied below:
function utf8_strrev( $str )
{
return implode( array_reverse( utf8_split( $str ) ) );
}
function utf8_split( $str , $split_length = 1 )
{
$str = ( string ) $str;
$ret = array( );
if( pcre_utf8_support( ) )
{
$str = utf8_clean( $str );
$ret = preg_split('/(?<!^)(?!$)/u', $str );
// \X is buggy in many recent versions of PHP
//preg_match_all( '/\X/u' , $str , $ret );
//$ret = $ret[0];
}
else
{
//Fallback
$len = strlen( $str );
for( $i = 0 ; $i < $len ; $i++ )
{
if( ( $str[$i] & "\x80" ) === "\x00" )
{
$ret[] = $str[$i];
}
else if( ( ( $str[$i] & "\xE0" ) === "\xC0" ) && ( isset( $str[$i+1] ) ) )
{
if( ( $str[$i+1] & "\xC0" ) === "\x80" )
{
$ret[] = $str[$i] . $str[$i+1];
$i++;
}
}
else if( ( ( $str[$i] & "\xF0" ) === "\xE0" ) && ( isset( $str[$i+2] ) ) )
{
if( ( ( $str[$i+1] & "\xC0" ) === "\x80" ) && ( ( $str[$i+2] & "\xC0" ) === "\x80" ) )
{
$ret[] = $str[$i] . $str[$i+1] . $str[$i+2];
$i = $i + 2;
}
}
else if( ( ( $str[$i] & "\xF8" ) === "\xF0" ) && ( isset( $str[$i+3] ) ) )
{
if( ( ( $str[$i+1] & "\xC0" ) === "\x80" ) && ( ( $str[$i+2] & "\xC0" ) === "\x80" ) && ( ( $str[$i+3] & "\xC0" ) === "\x80" ) )
{
$ret[] = $str[$i] . $str[$i+1] . $str[$i+2] . $str[$i+3];
$i = $i + 3;
}
}
}
}
if( $split_length > 1 )
{
$ret = array_chunk( $ret , $split_length );
$ret = array_map( 'implode' , $ret );
}
if( $ret[0] === '' )
{
return array( );
}
return $ret;
}
function utf8_clean( $str , $remove_bom = false )
{
$regx = '/([\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})|./s';
$str = preg_replace( $regx , '$1' , $str );
if( $remove_bom )
{
$str = utf8_str_replace( utf8_bom( ) , '' , $str );
}
return $str;
}
function utf8_str_replace( $search , $replace , $subject , &$count = 0 )
{
return str_replace( $search , $replace , $subject , $count );
}
function utf8_bom( )
{
return "\xef\xbb\xbf";
}
function pcre_utf8_support( )
{
static $support;
if( !isset( $support ) )
{
$support = #preg_match( '//u', '' );
//Cached the response
}
return $support;
}