Parse data into array - php

I'm creating a "quote database" for a TV show I'm a fan of, and I'm rewriting parts of it I don't particularly like. I came across my function to parse the data holding the quote and characters into an array that I can easily loop through and display. One of the features of the site is that you can have a single quote (one-liner) or a conversation between several characters. Right now I'm storing single quotes like this:
[charactername]This is my witty one-liner.
And conversations follow the same pattern:
[characternameone]How's the weather?
[characternametwo]Pretty good, actually.
And so on. Here's the aforementioned parsing function:
function parse_quote($text)
{
// Determine if it's a single or convo
if ( strpos($text, "\n") != false )
{
// Convo
// Let's explode into the separate characters/lines
$text = explode("\n", $text);
$convo = array();
// Parse each line into character and line
foreach ( $text as $part )
{
$character = substr($part, 1, strpos($part, ']') - 1);
$line = substr($part, strlen($character) + 2);
$convo[] = array(
'character' => $character,
'line' => $line
);
}
return array(
'type' => 'convo',
'quote' => $convo
);
}
else
{
// Single
// Parse line into character and line
return array(
'type' => 'single',
'quote' => array(
'character' => substr($text, 1, strpos($text, ']') - 1),
'line' => substr($text, strlen(substr($text, 1, strpos($text, ']') - 1)) + 2)
)
);
}
}
It works as expected, but I can't help but think there's a better way to do this. I'm horrible with regular expressions, which I assume would come in at least somewhat handy in this situation. Any advice, or improvements?

Personally, I would change your data storage method. It would be much easier to deal with a serialized or JSON encoded string.
Instead of
[characternameone]How's the weather?
[characternametwo]Pretty good, actually.
you would have
array(
[0] => {
'name' => "characternameone",
'quote' => "How's the weather?"
},
[1] => {
'name' => "characternametwo",
'quote' => "Pretty good, actually"
}
)
Then when you read it out, there isn't any parsing.
function display_quote($input)
{
for ($i=0, $n=count($input); $i<$n; $i++) {
$quote = $input[$i];
if ( $i > 0 ) echo "\n";
echo $quote['name'] . ': ' . $quote['quote'];
}
}

Instead of
$character = substr($part, 1, strpos($part, ']') - 1);
$line = substr($part, strlen($character) + 2);
$convo[] = array(
'character' => $character,
'line' => $line
);
you could try
preg_match('#\[([^\]]+)\](.*)#ism', $part, $match);
$convo[] = array(
'character' => $match[1],
'line' => $match[2]
);
HTH

Related

Loop function until there is no specified needle in the haystack

I'm trying to build a function, that will check if the needle is in haystack, if it is then the function should loop until there is none.
So far I've built a function for random number generation. What that function does is it will create a random string then hash it with sha512. The function then extracts all the numbers from a string and multiplies it by 2.
It leaves me with a long line of numbers, then I'll use mt_rand to specify start and end point for strpos. After that I just return the number that was created randomly.
function randomNumber($suurus, $max){
mb_internal_encoding("UTF-8");
$possible="aábcdeéfghiíjklmnoóöópqrstuúüűvwxyzAÁBCDEÉFGHIÍJKLMNOÓÖŐPQRSTUÚVWXYZ";
$char = mb_substr($possible,rand(0, mb_strlen($possible) - 1),1);
$str = openssl_digest($char, 'sha512');
$str = preg_replace('/[^1-9.]+/', '', $str);
$str = $str*2;
$strLen = strlen($str)-5;
$strStart = mt_rand(1, $strLen);
$strEnd = mt_rand(1, $suurus);
$str = substr($str, $strStart, $strEnd);
while($str > $max){
$str = $str / 2;
}
return round($str);
The thing is that this number can't repeat, I need it to be unique at all times for my app.
I actually accomplished what I wanted with if else statements, but that means I have to write a long script of repeated if statements until I get the results I want. I really don't think this is the smartest way to do things.
if(!isset($voitjad)){
$voitjad[$vCounter][] = array(
'nimi' => $voitja,
'auhind' => $keyCode['v'.$vCount.''],
'rn' => $rN,
'siin' => 1,
'id' => $voitjaID
);
}else{
if(!exist($rN, $voitjad)){
$voitjad[$vCounter][] = array(
'nimi' => $voitja,
'auhind' => $keyCode['v'.$vCount.''],
'rn' => $rN,
'siin' => 1,
'id' => $voitjaID
);
}else{
$rN = randomNumber($atLength, $atCount);
$voitja = $attendcheckfb['attending'][$rN]['name'];
$voitjaID = $attendcheckfb['attending'][$rN]['id'];
if(!exist($rN, $voitjad)){
$voitjad[$vCounter][] = array(
'nimi' => $voitja,
'auhind' => $keyCode['v'.$vCount.''],
'rn' => $rN,
'siin' => 2,
'id' => $voitjaID
);
}else{ and so on....
I also tried with do-while loop, but I couldn't really get it working, I'm not sure what exactly I'm doing wrong here. If you can educate me, shoot me with information.
$nimed = array_values($nimed);
$voitja = $nimed[$rN]['name'];
$voitjaID = $nimed[$rN]['id'];
$voitjad[$vCounter][] = array(
'nimi' => $voitja,
'auhind' => $keyCode['v'.$vCount.''],
'rn' => $rN,
'siin' => 1,
'id' => $voitjaID
);
$oitjaCount++;
$rN = randomNumber($nimedLength, $nimedCount);
} while(!exist($rN, $voitjad));
If there's a way for me to get better randomly generated numbers, then please tell me, I'm open for suggestions. Meanwhile I need help with this needle, haystack thing.
You can either do it with a loop:
function get_unique_hashcode() {
global $hash_array;
do {
$hashcode = make_random_hashcode();
} while (in_array($hashcode, $hash_array));
$hash_array[] = $hashcode;
return $hashcode;
}
Or you can do it with recursion:
function get_unique_hashcode() {
global $hash_array;
$hashcode = make_random_hashcode();
if (in_array($hashcode, $hash_array)) {
return get_unique_hashcode();
} else {
$hash_array[] = $hashcode;
return $hashcode;
}
}
This is just a general structure, you may need to adjust it for your detailed needs.

PHP get array of search terms from string

Is there an easy way to parse a string for search terms including negative terms?
'this -that "the other thing" -"but not this" "-positive"'
would change to
array(
"positive" => array(
"this",
"the other thing",
"-positive"
),
"negative" => array(
"that",
"but not this"
)
)
so those terms could be used to search.
The code below will parse your query string and split it up into positive and negative search terms.
// parse the query string
$query = 'this -that "-that" "the other thing" -"but not this" ';
preg_match_all('/-*"[^"]+"|\S+/', $query, $matches);
// sort the terms
$terms = array(
'positive' => array(),
'negative' => array(),
);
foreach ($matches[0] as $match) {
if ('-' == $match[0]) {
$terms['negative'][] = trim(ltrim($match, '-'), '"');
} else {
$terms['positive'][] = trim($match, '"');
}
}
print_r($terms);
Output
Array
(
[positive] => Array
(
[0] => this
[1] => -that
[2] => the other thing
)
[negative] => Array
(
[0] => that
[1] => but not this
)
)
For those looking for the same thing I have created a gist for PHP and JavaScript
https://gist.github.com/UziTech/8877a79ebffe8b3de9a2
function getSearchTerms($search) {
$matches = null;
preg_match_all("/-?\"[^\"]+\"|-?'[^']+'|\S+/", $search, $matches);
// sort the terms
$terms = [
"positive" => [],
"negative" => []
];
foreach ($matches[0] as $i => $match) {
$negative = ("-" === $match[0]);
if ($negative) {
$match = substr($match, 1);
}
if (($match[0] === '"' && substr($match, -1) === '"') || ($match[0] === "'" && substr($match, -1) === "'")) {
$match = substr($match, 1, strlen($match) - 2);
}
if ($negative) {
$terms["negative"][] = $match;
} else {
$terms["positive"][] = $match;
}
}
return $terms;
}

How can I replace multiple strings within a string without overlapping results?

I'm trying to create common masks from a string like so:
012abc.d+e_fg~hijk => 012{start}.d+{middle}_fg~{end}jk
replace:
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
Instead I keep overlapping replacements and get something like this instead (using a loop of str_replace's):
012{{pre}art}.d+{mi{post}le}_fg~{end}jk
Because the st is found in the already replaced {start} and dd is found in {middle}.
How would you replace the following?
$str = 'abc.d+e_fg~hijk';
echo replace_vars($str); // Desired output: 012{start}.d+{middle}_fg~{end}kJ
I might misunderstand, but you don't seem to need regex for the replacing. They're simple, literal replacements.
$from = '012abc.d+e_fg~hijk';
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$to = strtr($from, $arrFromTo); // 012{start}.d+{middle}_fg~{end}jk
strtr() is awesome. It takes a very readable input and it doesn't re-replace like your problem in the loop.
You can use preg_replace like this:
$str = '012abc.d+e_fg~hijk';
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$reArr=array();
foreach($arrFromTo as $k=>$v){
$reArr['/' . $k . '(?![^{}]*})/'] = $v;
}
echo preg_replace(array_keys($reArr), array_values($reArr), $str);
//=> 012{start}.d+{middle}_fg~{end}jk
Core of this regex is this negative lookaead: (?![^{}]*})
Which avoid matching keys of array if it is enclosed in {...} since all the replacements are enclosed in {...}.
This will search the string for each replacement in order. If it finds one, it will split the string, and search the remainder of the string for any other replacements.
$str = '012abc.d+e_fg~hijk';
$rep = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$searched = '';
foreach ($rep as $key => $r) {
if (strpos($str, $key) !== false) {
$searched .= substr($str, 0, strpos($str, $key)) . $r;
$str = substr($str, strpos($str, $key) + strlen($key));
}
}
$searched .= $str;
echo $searched; //012{start}.d+{middle}_fg~{end}jk
It will search and find them in the order that you have specified.

Print_r line bug ? \n?

I have this OUTPUT array from Decode function down:
Array ( [
] =>
[HostName] => Survival4fun
[GameType] => SMP
[Version] => 1.5.2
[Plugins] => Array
(
[0] => WorldEdit
)
[Map] => world
[Players] => 0
[MaxPlayers] => 10
[HostPort] => 25608
[HostIp] => 31.133.13.99
[RawPlugins] => WorldEdit5.5.6;
[Software] => CraftBukkitonBukkit1.5.2-R0.1
[Status] => online
[Ping] => 15ms
[
] =>
[PlayersOnline] => Array
(
[P0] => NoPlayers
)
[
] => )
And so, you can see this:
[
] =>
How can I remove it ? I tried using str_replace("\n", "", $arr); But this doesn't work.
Here is the original array - http://status.mc-host.cz/s8.mc-host.cz:25608-feed
And here is my function code:
Function Decode_query($link) {
$data = file($link, FILE_IGNORE_NEW_LINES);
$arr = array();
$string = array("[", "]", " ", "(", ")", "Array", "\n", "\r");
$replace = array("", "", "", "", "", "", "", "");
ForEach ($data as $line) {
$s = str_replace($string, $replace, $line);
If (Empty($s)) {} Else {
$stat = explode("=>", $s);
$P = str_replace("P", "", $stat[0]);
If (is_numeric($stat[0])) {
$arr["Plugins"][$stat[0]] = $stat[1];
}
ElseIf (is_numeric($P)) {
$arr['PlayersOnline'][$stat[0]] = $stat[1];
} Else {
$arr[$stat[0]] = $stat[1];
}
}
}
Return $arr;
}
$arr = Decode_query("http://status.mc-host.cz/s8.mc-host.cz:25608-feed");
Print_r($arr);
Thanks for help and sorry for long question..
You could use a regex to scan for keys that are composed of only whitespace:
$keys = array_keys($your_array);
$blank_keys = preg_grep('/^\s*$/', $keys);
foreach($blank_keys as $blank) {
unset($your_array[$blank]);
}
I would work with trim in stead of str_replace. It is less expensive, and it takes care of the trailing spaces and whatever whitespace there may be. In your case your function would probably look something like this:
Function Decode_query($link) {
// fetch the data
$data = file($link, FILE_IGNORE_NEW_LINES);
// prepare output array
$arr = array('Plugins' => array(), 'PlayersOnline' => array());
// prepare the list of characters we want to remove
$removeChars = ' \t\n\r[]';
ForEach ($data as $line) {
// split line into key, value
$stat = explode("=>", $line);
// no 2 elements, means no '=>', so ignore line
if (count($stat) < 2) continue;
// remove unwanted characters from key
$trimmed = trim($stat[0], $removeChars);
$pTrimmed = trim($trimmed, 'P');
// if key = plugins, ignore line
if ($trimmed == 'Plugins') continue;
// if key is numeric
If (is_numeric($trimmed)) {
// store in plugins subarray
$arr['Plugins'][$trimmed] = trim($stat[1]);
}
// if (key - P) is numeric
ElseIf (is_numeric($pTrimmed)) {
// store in players online subarray
$arr['PlayersOnline'][$pTrimmed] = trim($stat[1]);
} Else {
// all others store in level 1 array
$arr[$trimmed] = trim($stat[1]);
}
}
Return $arr;
}
I didn't test the code, but I think it should work fine.
PS: You can never put enough comments in your code, may seem a waste of time at first, but you, or anyone who has to work on your code, will be very grateful some day...

How to parse file with PHP

I have this file, I cant figure out how to parse this file.
type = 10
version = 1.2
PART
{
part = foobie
partName = foobie
EVENTS
{
MakeReference
{
active = True
}
}
ACTIONS
{
}
}
PART
{
part = bazer
partName = bazer
}
I want this to be a array which should look like
$array = array(
'type' => 10,
'version' => 1.2,
'PART' => array(
'part' => 'foobie',
'partName' => 'foobie,
'EVENTS' => array(
'MakeReference' => array(
'active' => 'True'
)
),
'ACTIONS' => array(
)
),
'PART' => array(
'part' => 'bazer',
'partName' => 'bazer'
)
);
I tried with preg_match but that was not a success.
Any ideas?
Why not use a format that PHP can decode natively, like JSON?
http://php.net/json_decode
$json = file_get_contents("filename.txt");
$array = json_decode($json);
print_r($array);
Here is my approach.
First of all change Im changing the { to the line before.
Thats pretty easy
$lines = explode("\r\n", $this->file);
foreach ($lines as $num => $line) {
if (preg_match('/\{/', $line) === 1) {
$lines[$num - 1] .= ' {';
unset($lines[$num]);
}
}
Now the input looks like this
PART {
part = foobie
Now we can make the whole thing to XML instead, I know that I said a PHP array in the question, but a XML object is still fine enough.
foreach ($lines as $line) {
if (preg_match('/(.*?)\{$/', $line, $matches)) {
$xml->addElement(trim($matches[1]));
continue;
}
if (preg_match('/\}/', $line)) {
$xml->endElement();
continue;
}
if (strpos($line, ' = ') !== false) {
list($key, $value) = explode(' = ', $line);
$xml->addElementAndContent(trim($key), trim($value));
}
}
The addElement, actually just adds a to a string
endElement adds a and addElementAndContent doing both and also add content between them.
And just for making the whole content being a XML object, im using
$xml = simplexml_load_string($xml->getXMLasText());
And now $xml is a XML object, which is so much easier to work with :)

Categories