consecutive occurrence of character - php

number of consecutive occurrence of character followed by character
Ex input : 'zzzyyyyxxxwwvzz'
expected output : '3z4y3x2w1v2z'
code that i have tried
<?php
$str = "zzzyyyyxxxwwvzz";
$strArray = count_chars($str,1);
foreach ($strArray as $key=>$value)
{
echo $value.chr($key);
}
?>
output is : 5z4y3x2w1v

Using str_split function, get all characters in the given string in an array
Now, using basic looping and conditionals, and storing previous character, you can determine if consecutive or not, and accordingly get the output string generated.
Try the following (explanation in code comments):
$input = 'zzzyyyyxxxwwvzz';
// Split full string into array of single characters
$input_chars = str_split($input);
// initialize some temp variables
$prev_char = '';
$consecutive_count = 0;
$output = '';
// Loop over the characters
foreach ($input_chars as $char) {
// first time initialize the previous character
if ( empty($prev_char) ) {
$prev_char = $char;
$consecutive_count++;
} elseif ($prev_char === $char) { // current character matches previous character
$consecutive_count++;
} else { // not consecutive character
// add to output string
$output .= ($consecutive_count . $prev_char);
// set current char as new previous_char
$prev_char = $char;
$consecutive_count = 1;
}
}
// handle remaining characters
$output .= ($consecutive_count . $prev_char);
echo $output;
Rextester Demo

Related

PHP Hiding Last 3 Chars Every Word

Example String:
AAAAAA BBBBB CCCCCCC
Output:
AAAXXX BBXXX CCCCXXX
I need to hide every last 3 chars of words. I tried str_replace but I can't make it. Thanks for help
$replacement = "***";
if (stripos($name, ' ') !== false) {
$star = substr($name, 0, -3).$replacement;
}
I tried this but code only hide last word's 3 char. I need every word. Thanks
You need to split your string up, replace the last three characters, and then reassemble it.
$replacement = "***";
// break your string into an array based on the spaces
$temp = explode(' ', $name) ;
// our temporary name
$newName = '' ;
// loop through each part of the original name
foreach($temp as $section) {
// append our modified string along with a space
$newName .= substr($section, 0, -3).$replacement . ' ' ;
}
// set $name = $newName without the trailing space
$name = substr($newName,0,-1) ;
$replacement = "***";
$star = "";
$pieces = explode(" ", $name);
foreach ($pieces as $piece){
$star .= substr($piece, 0, -3).$replacement." ";
}
Try this function,
function replaceLastThreeChars($string){
$reversed_string = strrev($string);
$replaced_string = str_replace(substr($reversed_string,0,3),"XXX",$reversed_string);
return strrev($replaced_string);
}
If you have a sentence, then you can slice it using spaces and call to this function and finally recreate the sentence.

PHP code to create a negative word dictionary and search if a post has negative words

I'm trying to develop a PHP application where it takes comments from users and then match the string to check if the comment is positive or negative. I have list of negative words in negative.txt file. If a word is matched from the word list, then I want a simple integer counter to increment by 1. I tried the some links and created the a code to check if the comment has is negative or positive but it is only matching the last word of the file.Here's the code what i have done.
<?php
function teststringforbadwords($comment)
{
$file="BadWords.txt";
$fopen = fopen($file, "r");
$fread = fread($fopen,filesize("$file"));
fclose($fopen);
$newline_ele = "\n";
$data_split = explode($newline_ele, $fread);
$new_tab = "\t";
$outoutArr = array();
//process uploaded file data and push in output array
foreach ($data_split as $string)
{
$row = explode($new_tab, $string);
if(isset($row['0']) && $row['0'] != ""){
$outoutArr[] = trim($row['0']," ");
}
}
//---------------------------------------------------------------
foreach($outoutArr as $word) {
if(stristr($comment,$word)){
return false;
}
}
return true;
}
if(isset($_REQUEST["submit"]))
{
$comments = $_REQUEST["comments"];
if (teststringforbadwords($comments))
{
echo 'string is clean';
}
else
{
echo 'string contains banned words';
}
}
?>
Link Tried : Check a string for bad words?
I added the strtolower function around both your $comments and your input from the file. That way if someone spells STUPID, instead of stupid, the code will still detect the bad word.
I also added trim to remove unnecessary and disruptive whitespace (like newline).
Finally, I changed the way how you check the words. I used a preg_match to split about all whitespace so we are checking only full words and don't accidentally ban incorrect strings.
<?php
function teststringforbadwords($comment)
{
$comment = strtolower($comment);
$file="BadWords.txt";
$fopen = fopen($file, "r");
$fread = strtolower(fread($fopen,filesize("$file")));
fclose($fopen);
$newline_ele = "\n";
$data_split = explode($newline_ele, $fread);
$new_tab = "\t";
$outoutArr = array();
//process uploaded file data and push in output array
foreach ($data_split as $bannedWord)
{
foreach (preg_split('/\s+/',$comment) as $commentWord) {
if (trim($bannedWord) === trim($commentWord)) {
return false;
}
}
}
return true;
}
1) Your storing $row['0'] only why not others index words. So problem is your ignoring some of word in text file.
Some suggestion
1) Insert the text in text file one by one i.e new line like this so you can access easily explode by newline to avoiding multiple explode and loop.
Example: sss.txt
...
bad
stupid
...
...
2) Apply trim and lowercase function to both comment and bad string.
Hope it will work as expected
function teststringforbadwords($comment)
{
$file="sss.txt";
$fopen = fopen($file, "r");
$fread = fread($fopen,filesize("$file"));
fclose($fopen);
foreach(explode("\n",$fread) as $word)
{
if(stristr(strtolower(trim($comment)),strtolower(trim($word))))
{
return false;
}
}
return true;
}

Trouble whith php rainbow text

I have trouble with php rainbow text functions.
When I run this function, the output text did not support vietnamese.
For example: "tôi yêu em" ;
<?php
function rainbow($text)
{
/*** initialize the return string ***/
$ret = '';
/*** an array of colors ***/
$colors = array(
'ff00ff', 'ff0099', 'ff0033', 'ff3300',
'ff9900', 'ffff00', '99ff00', '33ff00',
'00ff33', '00ff99', '00ffff', '0099ff',
'0033ff', '3300ff', '9900ff'
);
/*** a counter ***/
$i = 0;
/*** get the length of the text ***/
$textlength = strlen($text);
/*** loop over the text ***/
while($i<=$textlength)
{
/*** loop through the colors ***/
foreach($colors as $value)
{
if ($text[$i] != "")
{
$ret .= '<span style="color:#'.$value.';">'.$text[$i]."</span>";
}
$i++;
}
}
/*** return the highlighted string ***/
$ret = html_entity_decode($ret, ENT_QUOTES, 'UTF-8');
return $ret;
}
echo rainbow('tôi yêu em');
?>
You're going to get uninitialized string offset notices in any language with that function due to the way you're iterating over the string bytes + colors. Better to access the colors via an InfiniteIterator which will just loop around and around.
Your specific problem with Vietnamese is that some of those characters are composed of multiple bytes. Functions like strlen() and accessing offsets via array brackets like $text[$i] are not multi-byte safe - they work on individual bytes rather than characters.
While it might be tempting to just use mb_strlen() in place of strlen() to get the number of characters rather than the number of bytes, and mb_substr() rather than $text[$i] to get a character rather than a byte, you'll still end up breaking up graphemes like è (which is here encoded as e followed by a combining grave accent.) A solution is to break up the string into an array with a regular expression.
Example:
function rainbow($text)
{
$text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
$return = '';
$colors = new InfiniteIterator(
new ArrayIterator(
['ff00ff', 'ff0099', 'ff0033', 'ff3300',
'ff9900', 'ffff00', '99ff00', '33ff00',
'00ff33', '00ff99', '00ffff', '0099ff',
'0033ff', '3300ff', '9900ff']
)
);
$colors->rewind();
// Match any codepoint along with any combining marks.
preg_match_all('/.\pM*+/su', $text, $matches);
foreach ($matches[0] as $char)
{
if (preg_match('/^\pZ$/u', $char)) {
// No need to color whitespace or invisible separators.
$return .= $char;
} else {
$return .= "<span style='color:#{$colors->current()};'>$char</span>";
$colors->next();
}
}
return $return;
}
echo rainbow('tôi yêu em evè foo baz');
Output:
<span style='color:#ff00ff;'>t</span><span style='color:#ff0099;'>ô</span><span style='color:#ff0033;'>i</span> <span style='color:#ff3300;'>y</span><span style='color:#ff9900;'>ê</span><span style='color:#ffff00;'>u</span> <span style='color:#99ff00;'>e</span><span style='color:#33ff00;'>m</span> <span style='color:#00ff33;'>e</span><span style='color:#00ff99;'>v</span><span style='color:#00ffff;'>è</span> <span style='color:#0099ff;'>f</span><span style='color:#0033ff;'>o</span><span style='color:#3300ff;'>o</span> <span style='color:#9900ff;'>b</span><span style='color:#ff00ff;'>a</span><span style='color:#ff0099;'>z</span>

Place content in between paragraphs without images

I am using the following code to place some ad code inside my content .
<?php
$content = apply_filters('the_content', $post->post_content);
$content = explode (' ', $content);
$halfway_mark = ceil(count($content) / 2);
$first_half_content = implode(' ', array_slice($content, 0, $halfway_mark));
$second_half_content = implode(' ', array_slice($content, $halfway_mark));
echo $first_half_content.'...';
echo ' YOUR ADS CODE';
echo $second_half_content;
?>
How can i modify this so that the 2 paragraphs (top and bottom) enclosing the ad code should not be the one having images. If the top or bottom paragraph has image then try for next 2 paragraphs.
Example: Correct Implementation on the right.
preg_replace version
This code steps through every paragraph ignoring those that contain image tags. The $pcount variable is incremented for every paragraph found without an image, if an image is encountered however, $pcount is reset to zero. Once $pcount reaches the point where it would hit two, the advert markup is inserted just before that paragraph. This should leave the advert markup between two safe paragraphs. The advert markup variable is then nullified so only one advert is inserted.
The following code is just for set up and could be modified to split the content differently, you could also modify the regular expression that is used — just in case you are using double BRs or something else to delimit your paragraphs.
/// set our advert content
$advert = '<marquee>BUY THIS STUFF!!</marquee>' . "\n\n";
/// calculate mid point
$mpoint = floor(strlen($content) / 2);
/// modify back to the start of a paragraph
$mpoint = strripos($content, '<p', -$mpoint);
/// split html so we only work on second half
$first = substr($content, 0, $mpoint);
$second = substr($content, $mpoint);
$pcount = 0;
$regexp = '/<p>.+?<\/p>/si';
The rest is the bulk of the code that runs the replacement. This could be modified to insert more than one advert, or to support more involved image checking.
$content = $first . preg_replace_callback($regexp, function($matches){
global $pcount, $advert;
if ( !$advert ) {
$return = $matches[0];
}
else if ( stripos($matches[0], '<img ') !== FALSE ) {
$return = $matches[0];
$pcount = 0;
}
else if ( $pcount === 1 ) {
$return = $advert . $matches[0];
$advert = '';
}
else {
$return = $matches[0];
$pcount++;
}
return $return;
}, $second);
After this code has been executed the $content variable will contain the enhanced HTML.
PHP versions prior to 5.3
As your chosen testing area does not support PHP 5.3, and so does not support anonymous functions, you need to use a slightly modified and less succinct version; that makes use of a named function instead.
Also, in order to support content that may not actually leave space for the advert in it's second half I have modified the $mpoint so that it is calculated to be 80% from the end. This will have the effect of including more in the $second part — but will also mean your adverts will be generally placed higher up in the mark-up. This code has not had any fallback implemented into it, because your question does not mention what should happen in the event of failure.
$advert = '<marquee>BUY THIS STUFF!!</marquee>' . "\n\n";
$mpoint = floor(strlen($content) * 0.8);
$mpoint = strripos($content, '<p', -$mpoint);
$first = substr($content, 0, $mpoint);
$second = substr($content, $mpoint);
$pcount = 0;
$regexp = '/<p>.+?<\/p>/si';
function replacement_callback($matches){
global $pcount, $advert;
if ( !$advert ) {
$return = $matches[0];
}
else if ( stripos($matches[0], '<img ') !== FALSE ) {
$return = $matches[0];
$pcount = 0;
}
else if ( $pcount === 1 ) {
$return = $advert . $matches[0];
$advert = '';
}
else {
$return = $matches[0];
$pcount++;
}
return $return;
}
echo $first . preg_replace_callback($regexp, 'replacement_callback', $second);
You could try this:
<?php
$ad_code = 'SOME SCRIPT HERE';
// Your code.
$content = apply_filters('the_content', $post->post_content);
// Split the content at the <p> tags.
$content = explode ('<p>', $content);
// Find the mid of the article.
$content_length = count($content);
$content_mid = floor($content_length / 2);
// Save no image p's index.
$last_no_image_p_index = NULL;
// Loop beginning from the mid of the article to search for images.
for ($i = $content_mid; $i < $content_length; $i++) {
// If we do not find an image, let it go down.
if (stripos($content[$i], '<img') === FALSE) {
// In case we already have a last no image p, we check
// if it was the one right before this one, so we have
// two p tags with no images in there.
if ($last_no_image_p_index === ($i - 1)) {
// We break here.
break;
}
else {
$last_no_image_p_index = $i;
}
}
}
// If no none image p tag was found, we use the last one.
if (is_null($last_no_image_p_index)) {
$last_no_image_p_index = ($content_length - 1);
}
// Add ad code here with trailing <p>, so the implode later will work correctly.
$content = array_slice($content, $last_no_image_p_index, 0, $ad_code . '</p>');
$content = implode('<p>', $content);
?>
It will try to find a place for the ad from the mid of your article and if none is found the ad is put to the end.
Regards
func0der
I think this will work:
First explode the paragraphs, then you have to loop it and check if you find img inside them.
If you find it inside, you try the next.
Think of this as psuedo-code, since it's not tested. You will have to make a loop too, comments in the code :) Sorry if it contains bugs, it's written in Notepad.
<?php
$i = 0; // counter
$arrBoolImg = array(); // array for the paragraph booleans
$content = apply_filters('the_content', $post->post_content);
$contents = str_replace ('<p>', '<explode><p>', $content); // here we add a custom tag, so we can explode
$contents = explode ('<explode>', $contents); // then explode it, so we can iterate the paragraphs
// fill array with boolean array returned
$arrBoolImg = hasImages($contents);
$halfway_mark = ceil(count($contents) / 2);
/*
TODO (by you):
---
When you have $arrBoolImg filled, you can itarate through it.
You then simply loop from the middle of the array $contents (plural), that is exploded from above.
The startingpoing for your loop is the middle, the upper bounds is the +2 or what ever :-)
Then you simply insert your magic.. And then glue it back together, as you did before.
I think this will work. even though the code may have some bugs, since I wrote it in Notepad.
*/
function hasImages($contents) {
/*
This function loops through the $contents array and checks if they have images in them
The return value, is an array with boolean values, so one can iterate through it.
*/
$arrRet = array(); // array for the paragraph booleans
if (count($content)>=1) {
foreach ($contents as $v) { // iterate the content
if (strpos($v, '<img') === false) { // did not find img
$arrRet[$i] = false;
}
else { // found img
$arrRet[$i] = true;
}
$i++;
} // end for each loop
return $arrRet;
} // end if count
} // end hasImages func
?>
[This is just an idea, I don't have enough reputation to comment...]
After calling #Olavxxx's method and filling your boolean array you could just loop through that array in an alternating manner starting in the middle: Let's assume your array is 8 entries long. Calculating the middle using your method you get 4. So you check the combination of values 4 + 3, if that doesn't work, you check 4 + 5, after that 3 + 2, ...
So your loop looks somewhat like
$middle = ceil(count($content) / 2);
$i = 1;
while ($i <= $middle) {
$j = $middle + (-1) ^ $i * $i;
$k = $j + 1;
if (!$hasImagesArray[$j] && !$hasImagesArray[$k])
break; // position found
$i++;
}
It's up to you to implement further constraints to make sure the add is not shown to far up or down in the article...
Please note that you need to take care of special cases like too short arrays too in order to prevent IndexOutOfBounds-Exceptions.

Finding all matches of string, and also returning line number of match

I have a string variable which contains some text (shown below). The text has line breaks in it as shown. I would like to search the text for a given string, and return the number of matches per line number. For instance, searching for "keyword" would return 1 match on line 3 and 2 matches on line 5.
I have tried using strstr(). It does a good job finding the first match, and giving me the remaining text, so I can do it again and again until there are no matches. Problem is I do not know how to determine which line number the match occurred on.
Hello,
This is some text.
And a keyword.
Some more text.
Another keyword! And another keyword.
Goodby.
Why not split the text on line-feeds and loop, use the index + 1 as a line number:
$txtParts = explode("\n",$txt);
for ($i=0, $length = count($txtParts);$i<$length;$i++)
{
$tmp = strstr($txtParts[$i],'keyword');
if ($tmp)
{
echo 'Line '.($i +1).': '.$tmp;
}
}
Tested, and working. Just a quick tip, since you're looking for matches in a text (sentences, upper- and lower-case etc...) perhaps stristr (case-insensitive) would be better?An example with foreach and stristr:
$txtParts = explode("\n",$txt);
foreach ($txtParts as $number => $line)
{
$tmp = stristr($line,'keyword');
if ($tmp)
{
echo 'Line '.($number + 1).': '.$tmp;
}
}
With this code you can have all data in one array (Linenumber and position numbers)
<?php
$string = "Hello,
This is some text.
And a keyword.
Some more text.
Another keyword! And another keyword.
Goodby.";
$expl = explode("\n", $string);
$linenumber = 1; // first linenumber
$allpos = array();
foreach ($expl as $str) {
$i = 0;
$toFind = "keyword";
$start = 0;
while($pos = strpos($str, $toFind, $start)) {
//echo $toFind. " " . $pos;
$start = $pos+1;
$allpos[$linenumber][$i] = $pos;
$i++;
}
$linenumber++; // linenumber goes one up
}
foreach ($allpos as $linenumber => $position) {
echo "Linenumber: " . $linenumber . "<br/>";
foreach ($position as $pos) {
echo "On position: " .$pos . "<br/>";
}
echo "<br/>";
}
Angelo's answer definitely provides more functionality and is probably the best answer, but the following is simple and seems to work. I will continue to play with all solutions.
function findMatches($text,$phrase)
{
$list=array();
$lines=explode("\n", $text);
foreach($lines AS $line_number=>$line)
{
str_replace($phrase,$phrase,$line,$count);
if($count)
{
$list[]='Found '.$count.' match(s) on line '.($line_number+1);
}
}
return $list;
}

Categories