Solving 140 characters Twitter status limit with PHP regex - php

So, my text I want to post on Twitter is sometimes more than 140 character, so, I need to check the lenght and then go without changes if less than 140 or slive the text into two pieces (the text and the link) and grab the text part and make it e.g. 100 characters long - chop the rest.
Then grab the - now 100 characters long part - and put it otgether with the url.
How to do that?
my code so far:
if (strlen($status) < 140) {
// continue
} else {
// 1. slice the $status into $text and $url (every message has url so
// checking is not important right now
// 2. shorten the text to 100 char
// something like $text = substr($text, 0, 100); ?
// 3. put them back together
$status = $text . ' ' . $url;
}
How should I change my code? I have biggest problem with the first part when getting the url and text part.
Btw. in each $status is only 1 url, so checking for mulitple urls is not necessary
Example of a text that is longer than it should be:
What is now Ecuador was home to a variety of indigenous groups that were gradually incorporated into the Inca Empire during the fifteenth century. The territory was colonized by Spain during the sixteenth century, achieving independence in 1820 as part of Gran Colombia, from which it emerged as its own sovereign state in 1830. The legacy of both empires is reflected in Ecuador's ethnically diverse population, with most of its 15.2 million people being mestizos, followed by large minorities of European, Amerindian, and African descendant. https://en.wikipedia.org/wiki/Ecuador
should become in the end this:
What is now Ecuador was home to a variety of indigenous groups that were gradually incorporated int https://en.wikipedia.org/wiki/Ecuador

If you can be sure that the URL does not contain any spaces (no well-formed URL should) and that it is always present, try it like that:
preg_match('/^(.*)(\\S+)$/', $status, $matches);
$text = $matches[1];
$url = $matches[2];
$text = substr($text, 0, 100);
But possibly the length of the text should be adapted to the length of the url, so you would use
$text = substr($text, 0, 140-strlen($url)-1);

$reg = '/\b(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)[-A-Z0-9+&##\/%=~_|$?!:,.]*[A-Z0-9+&##\/%=~_|$]/i';
$string = "What is now Ecuador was home to a variety of indigenous groups that were gradually incorporated into the Inca Empire during the fifteenth century. The territory was colonized by Spain during the sixteenth century, achieving independence in 1820 as part of Gran Colombia, from which it emerged as its own sovereign state in 1830. The legacy of both empires is reflected in Ecuador's ethnically diverse population, with most of its 15.2 million people being mestizos, followed by large minorities of European, Amerindian, and African descendant. https://en.wikipedia.org/wiki/Ecuador";
preg_match_all($reg, $string, $matches, PREG_PATTERN_ORDER);
$cut_string = substr($string, 0, (140-strlen($matches[0][0])-1));
$your_twitt = $cut_string . " " . $matches[0][0];
echo $your_twitt;
// ouputs : "What is now Ecuador was home to a variety of indigenous groups that were gradually incorporated into t https://en.wikipedia.org/wiki/Ecuador"

This might be what you want :
$status = 'What is now Ecuador was home to a variety of indigenous groups that were gradually incorporated into the Inca Empire during the fifteenth century. The territory was colonized by Spain during the sixteenth century, achieving independence in 1820 as part of Gran Colombia, from which it emerged as its own sovereign state in 1830. The legacy of both empires is reflected in Ecuador\'s ethnically diverse population, with most of its 15.2 million people being mestizos, followed by large minorities of European, Amerindian, and African descendant. https://en.wikipedia.org/wiki/Ecuador';
if (strlen($status) < 140) {
echo 'Lenght ok';
} else {
$totalPart = round(strlen($status)/100);
$fulltweet = array();
for ($i=0; $i < $totalPart; $i++) {
if($i==0)
{
$fulltweet[$i] = substr($status, 0,100);
}else{
$fulltweet[$i] = substr($status, $i * 100);
}
}
}
If the string is longer than 140 chars then it'll explode it in an array of 100 char for each row

Related

Using preg_replace to reformat money amounts in text with PHP

I'm struggling with some regular expressions. What I want to do is find money amounts in a string, remove the €,$, or £ but keep the number, and then look to see if there is a 'b' or an 'm' - in which case write 'million platinum coins' or 'million gold coin' respectively but otherwise just put 'gold coins'.
I have most of that as a hack (see below) with the small problem that my regex does not seem to work. The money amount comes out unchanged.
Desired behaviour examples
I intend to leave the decimal places and thousands separators as is
$12.6m ==> 12.6 million gold coins
£2b ==> 2 million platinum coins
€99 ==> 99 gold coins
My code
Here is my non-working code (I suspect my regex might be wrong).
protected function funnymoney($text){
$text = preg_replace('/[€$£]*([0-9\.,]+)([mb])/i','\0 %\1%',$text);
$text = str_replace('%b%','million platnum coins',$text);
$text = str_replace('%m%','million gold coins',$text);
$text = str_replace('%%','gold coins',$text);
return $text;
}
I would greatly appreciate it if someone could explain to me what I am missing or getting wrong and guide me to the right answer. You may safely assume I know very little about regular expressions. I would like to understand why the solution works too if I can.
Using preg_replace_callback, you can do this in a single function call:
define ("re", '/[€$£]*(\.\d+|\d+(?:[.,]\d+)?)([mb]|)/i');
function funnymoney($text) {
return preg_replace_callback(re, function($m) {
return $m[1] .
($m[2] != "" ? " million" : "") . ($m[2] == "b" ? " platinum" : " gold") .
" coins";
}, $text);
}
// not call this function
echo funnymoney('$12.6m');
//=> "12.6 million gold coins"
echo funnymoney('£2b');
//=> "2 million platinum coins"
echo funnymoney('€99');
//=> "99 gold coins"
I am not sure how you intend to handle decimal places and thousands separators, so that part of my pattern may require adjustment. Beyond that, match the leading currency symbol (so that it is consumed/removed, then capture the numeric substring, then capture the optional trailing units (b or m).
Use a lookup array to translate the units to English. When the unit character is missing, apply the fallback value from the lookup array.
A lookup array will make your task easier to read and maintain.
Code: (Demo)
$str = '$1.1m
Foo
£2,2b
Bar
€99.9';
$lookup = [
'b' => 'million platinum coins',
'm' => 'million gold coins',
'' => 'gold coins',
];
echo preg_replace_callback(
'~[$£€](\d+(?:[.,]\d+)?)([bm]?)~iu',
function($m) use ($lookup) {
return "$m[1] " . $lookup[strtolower($m[2])];
},
$str
);
Output:
1.1 million gold coins
Foo
2,2 million platinum coins
Bar
99.9 gold coins
Your regex has a first full match on the string, and it goes on index 0 of the returning array, but it seems you just need the capturing groups.
$text = preg_replace('/[€$£]*([0-9\.,]+)([mb])/i','\1 %\2%',$text);
Funny question, btw!
Is this what you want?
<?php
/**
$12.6m ==> 12.6 million gold coins
£2b ==> 2 million platinum coins
€99 ==> 99 gold coins
*/
$str = <<<EOD
$12.6m
£2b
€99
EOD;
preg_match('/\$(.*?)m/', $str, $string1);
echo $string1[1] . " million gold coins \n";
preg_match('/\£(.*?)b/', $str, $string2);
echo $string2[1] . " million platinum coins \n";
preg_match('/\€([0-9])/', $str, $string3);
echo $string3[1] . " gold coins \n";
// output:
// 12.6 million gold coins
// 2 million platinum coins
// 9 gold coins

How to remove first </p> and last <p> tag in a string

I have a string like this:
$str = '<div class="content"><br />
<strong>0730</strong> – Check in direct to Compass at Marlin Wharf Berth 18</p> <p><strong>0800 </strong> – Depart Marlin Marina Cairns to the Great Barrier Reef</p>
<p><strong>1015</strong> – Arrive at your first Great Barrier Reef Location</p> <p><strong>1230</strong> – BBQ Lunch with fresh salads</p>
<p><strong>1300</strong> Cruise to 2nd Reef Location 1530 – Depart the Great Barrier Reef</p>
<p><strong>1730</strong> – Approximately Arrival time at Cairns Marina<br /> </div>';
I want to use preg_replace function to remove first </p> and last <p> tag because they are redundant, I have used this pattern but it didn't work.
$patterns = array(
'#^\s*</p>#',
'#<p>\s*$#',
);
$str = preg_replace( $patterns, '', $str );
You could do this with one expression:
$str = preg_replace("#(.*?)</p>(.*)<p>(.*)#s", "$1$2$3", $str, 1 );
This will do a non-greedy capture of text before the first </p>, then capture text greedily until <p> (which will be the last one because of the greediness). And finally the remaining text is also captured. The three captured groups are maintained, the 2 tags are not.
The s modifier is needed to allow the dot to also match new line characters.
Note that this does not check whether the removal is actually needed. It just does it, so if the HTML was already OK, you will get an non-desirable result.
This should do what you need
$str = '<div class="content"><br />
<strong>0730</strong> – Check in direct to Compass at Marlin Wharf Berth 18</p> <p><strong>0800 </strong> – Depart Marlin Marina Cairns to the Great Barrier Reef</p>
<p><strong>1015</strong> – Arrive at your first Great Barrier Reef Location</p> <p><strong>1230</strong> – BBQ Lunch with fresh salads</p>
<p><strong>1300</strong> Cruise to 2nd Reef Location 1530 – Depart the Great Barrier Reef</p>
<p><strong>1730</strong> – Approximately Arrival time at Cairns Marina<br /> </div>';
//Replace the first one, easy enough
$str = preg_replace('/<\/p>/', "", $str, 1);
$stringReplace = "<p>";
$stringLen = strlen($stringReplace);
//Get the position of the last one, with strrpos (reverse check)
$pos = strrpos($str, $stringReplace);
//Make sure there is one
if($pos !== false){
//If so, replace it with nothing
$str = substr_replace($str, "", $pos, $stringLen);
}
you can use something like this. but i don't check it myself. so let me and others know if it works or not.
$text = "Quick \"brown fox jumps \"over\" the lazy\" dog";
$resault = Regex.Replace(text, "(?<=^[^\"]*)\"|\"(?=[^\"]*$)", "\"\"\"");

preg_replace_callback highlight pattern not match in result

I have this code:
$string = 'The quick brown fox jumped over the lazy dog and lived to tell about it to his crazy moped.';
$text = explode("#", str_replace(" ", " #", $string)); //ugly trick to preserve space when exploding, but it works (faster than preg_split)
foreach ($text as $value) {
echo preg_replace_callback("/(.*p.*e.*d.*|.*a.*y.*)/", function ($matches) {
return " <strong>".$matches[0]."</strong> ";
}, $value);
}
The point of it is to be able to enter a sequence of characters (in the code above it's a fixed pattern), and it finds and highlights those characters in the matched word. The code I have now highlights the entire word. I'm looking for the most efficient way of highlighting the characters.
The result of the current code:
The quick brown fox jumped over the lazy dog and lived to tell about it to his crazy moped.
What I would like to have:
The quick brown fox jumped over the lazy dog and lived to tell about it to his crazy moped.
Did I take the wrong approach? It would be awesome if someone could point me in the right way, I've been searching for hours and didn't find what I was looking for.
EDIT 2:
Divaka's been a great help. Almost there.. I apologize if I haven't been clear enough on what my goal is. I will try to explain further.
- Part A -
One of the things I will be using this code for is a phone book. A simple example:
When following characters are entered:
Jan
I need it to match following examples:
Jan Verhoeven
Arjan Peters
Raj Naren
Jered Von Tran
The problem is that I will be iterating over the entire phone book, person-record per person-record. Each person also has email-addresses, a postal address, maybe a website, a extra note, ect.. This means that the text I'm actually search can contain anything from letters, numbers, special characters(&#()%_- etc..), newlines, and most importantly spaces. So an entire record (csv) might contain the following info:
Name;Address;Email address;Website;Note
Jan Verhoeven;Veldstraat 2a, 3209 Herkstad;jan#werk.be;www.janophetwerk.be,jan#telemet.be;Jan die ik ontmoet heb op de bouwbeurs.\n Zelfstandige vertegenwoordiger van bouwmaterialen.
Raj Naren;Kerklaan 334, 5873 Biep;raj#werk.be;;Rechtstreekse contactpersoon bij Werk.be (#654 intern)
The \n is meant to be an actual newline. So if I search for #werk.be, I'd like to see both these records as a result.
- Part B -
Something else I want to use this for is searching song-texts. When I'm looking for a song and I can only remember it had to do something with ducks or docks and a circle, I would enter dckcircle and get the following result:
... and the ducks were all dancing in a great big circle, around the great big bonfire ...
To be able to fine-tune the searching I'd like to be able to limit the number of spaces (or any other character), because I would imagine it finding a simple pattern like eve in every song while I'm only looking for a song that has the exact word eve in it.
- Conclusion -
If I summarize this in pseudo-regex, for a search pattern abc with a max of 3 spaces in-between it would be something like this: (I might be totally off here)
(a)(any character, max 3 spaces)(b)(any character, max 3 spaces)(c)
Or more generic:
(a)({any character}{these characters with a limit of 3})(b)({any character}{these characters with a limit of 3})(c)
This can even be extended to this fairly easily I'm guessing:
(a)({any character}{these characters with a limit of 3}{not these characters})(b)({any character}{these characters with a limit of 3}{not these characters})(c)
(I know the ´{}´ brackets are not to be used that way in a regular expression, but I don't know how else to put it without using a character that has a meaning in regular expressions.)
If anyone wonders, I know the sql like statement would be able to do 80% (I'm guessing, might even be more) of what I'm trying to do, but I'm trying to avoid using a database to make this as portable as possible.
When the correct answer has been found, I'll clean this question (and the code) up and post the resulting php-class here (maybe I'll even put it up on github if that would be useful), so anyone looking for the same will have a fully working class to work with :).
I've came up with this. Tell me if it's what you want!
//$string = "The quick brown fox jumped over the lazy dog and lived to tell about it to his crazy moped.";
$string = "abcdefo";
//$pattern_array1 = array(a,y);
//$pattern_array2 = array(p,e,d);
$pattern_array1 = array(e,f);
$pattern_array2 = array(o);
$pattern_array2 = array(a,f);
$number_of_patterns = 2;
$regexp1 = generate_regexp($pattern_array1, 1);
$regexp2 = generate_regexp($pattern_array2, 2);
$string = preg_replace($regexp1["pattern"], $regexp1["replacement"], $string);
$string = preg_replace($regexp2["pattern"], $regexp2["replacement"], $string);
$string = transform_multimatched_chars($string);
// transforming other chars after transforming the multimatched ones
for($i = 1; $i <= $number_of_patterns; $i++) {
$string = str_replace("#{$i}", "<strong>", $string);
$string = str_replace("#/{$i}", "</strong>", $string);
}
echo $string;
function generate_regexp($pattern_array, $pattern_num) {
$regexp["pattern"] = "/";
$regexp["replacement"] = "";
$i = 0;
foreach($pattern_array as $key => $char) {
$regexp["pattern"] .= "({$char})";
$regexp["replacement"] .= "#{$pattern_num}\$". ($key + $i+1) . "#/{$pattern_num}";
if($key < count($pattern_array) - 1) {
$regexp["pattern"] .= "(?s)((?:(?!{$pattern_array[$key + 1]})(?!\s).)*)";
$regexp["replacement"] .= "\$".($key + $i+2) . "";
}
$i = $key + 1;
}
$regexp["pattern"] .= "/";
return $regexp;
}
function transform_multimatched_chars($string)
{
preg_match_all("/((#[0-9]){2,})(.*)((#\/[0-9]){2,})/", $string, $matches);
// change this for your purposes
$start_replacement = '<span style="color:red;">';
$end_replacement = '</span>';
foreach($matches[1] as $key => $match)
{
$string = str_replace($match, $start_replacement, $string);
$string = str_replace($matches[4][$key], $end_replacement, $string);
}
return $string;
}

Find a pattern within two or more sets of text

I have lots of data that I need to search through for certain patterns.
Problem is when looking for said patterns I have no reference to what I'm looking for.
Or in other words, I have two paragraphs. Each on similar topics. I need to be able to compare both paragraphs and find patterns. Phrases said in both paragraphs and how many times both were said.
Can't seem to find the solution because preg_match and other functions your required to supply the things your looking for.
Example paragraphs
Paragraph 1:
Bee Pollen is made by honeybees, and is the food of the young bee. It
is considered one of nature's most completely nourishing foods as it
contains nearly all nutrients required by humans. Bee-gathered pollens
are rich in proteins (approximately 40% protein), free amino acids,
vitamins, including B-complex, and folic acid.
Paragraph 2:
Bee Pollen is made by honeybees. It is required for the fertilization
of the plant. The tiny particles consist of 50/1,000-millimeter
corpuscles, formed at the free end of the stamen in the heart of the
blossom, nature's most completely nourishing foods. Every variety of
flower in the universe puts forth a dusting of pollen. Many orchard
fruits and agricultural food crops do, too.
So from those examples these patterns:
Bee Pollen is made by honeybees
and:
nature's most completely nourishing foods
Both phrases are found in both paragraphs.
This is potentially a complex question depending on whether you're looking for similar phrases or phrases that match word for word.
Finding exact word-for-word matches is quite simple all you need to do is split on common breaks like punctuation marks (e.g. .,;:) and perhaps on conjunctions as well (e.g. and or). However, the problem comes when you come to, for example, adjectives two phrases might be exactly the same but have one word different, like so:
The world is spinnnig around its axis at a tremendous speed.
The world is spinning around its axis at a magnificent speed.
This won't match because tremendous and magnificent are used in place of one another. Potentially you could work around this, however, that would be a more complex question.
Answer
If we stick to the simple side of things we can achieve phrase matching with just a few lines of code (4 in this example; not including the formatting for comments/readability).
$wordSplits = 'and or on of as'; //List of words to split on
preg_match_all('/(?<m1>.*?)([.,;:\-]| '.str_replace(' ', ' | ', trim($wordSplits)).' )/i', $para1, $matches1);
preg_match_all('/(?<m2>.*?)([.,;:\-]| '.str_replace(' ', ' | ', trim($wordSplits)).' )/i', $para2, $matches2);
$commonPhrases = array_filter( //Removes blank $key=>$value pairs
array_intersect( //Finds matching paterns
array_map(function($item){
return(strtolower(trim($item))); //Cleans array for $para1 values - removes leading and following spaces
}, $matches1['m1']),
array_map(function($item){
return(strtolower(trim($item))); //Cleans array for $para2 values - removes leading and following spaces
}, $matches2['m2'])
)
);
var_dump($commonPhrases);
/**
OUTPUT:
array(2) {
[0]=>
string(31) "bee pollen is made by honeybees"
[5]=>
string(41) "nature's most completely nourishing foods"
}
/*
The above code will find matches splitting both on punctuation (defined in [...] of the preg_match_all pattern) it will also concatenate the word list (matching only words in the word list with a preceding and following space).
Wordlist
You can change the word list to include any breaks you like, editing the list until you get the phrases you are after, examples:
$wordSplits = 'and or';
$wordSplits = 'and but if or';
$wordSplits = 'a an as and by but because if in is it of off on or';
Punctuation
You can add any punctuation marks you like into the list (between [ and ]), however remember that some characters do have special meanings and may need to be escaped (or placed appropriately): - and ^ should become \- and \^ or be placed where their special meaning doesn't come into play.
You may consider changing:
([.,;:\-]|
To:
([.,;:\-] | //Adding a space before the pipe
So that you only split punctuation marks which are followed by a space. For example: this would mean that items like 50,000 won't be split.
Spaces and breaks
You may also consider changing the spaces to \s so that tabs and newlines etc are included and not just spaces. Like so:
'/(?<m1>.*?)([.,;:\-]|\s'.str_replace(' ', '\s|\s', trim($wordSplits)).'\s)/i'
This would also apply to:
([.,;:\-]\s|
If you decide to go down that route.
I've been working on this code, don't know if it suits your needs... Feel free to expand it!
$p1 = "Bee Pollen is made by honeybees, and is the food of the young bee. It is considered one of nature's most completely nourishing foods as it contains nearly all nutrients required by humans. Bee-gathered pollens are rich in proteins (approximately 40% protein), free amino acids, vitamins, including B-complex, and folic acid.";
$p2 = "Bee Pollen is made by honeybees. It is required for the fertilization of the plant. The tiny particles consist of 50/1,000-millimeter corpuscles, formed at the free end of the stamen in the heart of the blossom, nature's most completely nourishing foods. Every variety of flower in the universe puts forth a dusting of pollen. Many orchard fruits and agricultural food crops do, too.";
// Strip strings of periods etc.
$p1 = strtolower(str_replace(array('.', ',', '(', ')'), '', $p1));
$p2 = strtolower(str_replace(array('.', ',', '(', ')'), '', $p2));
// Extract words from first paragraph
$w1 = explode(" ", $p1);
// Build search string
$search = '';
$found = array();
foreach ($w1 as $word) {
//echo 'Word: ' . $word . "<br />";
$search .= ' ' . $word;
$search = trim($search);
//echo '. . Search string: '. $search . "<br /><br />";
if (substr_count($p2, $search)) {
$old_search = $search;
$num_occured = substr_count($p2, $search);
//echo " . . . found!" . "<br /><br /><br />";
$add = TRUE;
} else {
//echo " . . . not found! Generating new search string: " . $word . '<br />';
if ($add) {
$found[] = array('pattern' => $old_search, 'occurences' => $num_occured);
$add = FALSE;
}
$old_search = '';
$search = $word;
}
}
print_r($found);
The above code finds occurences of patterns from the first string in the second one.
I'm sure it can be written better, but since it's past midnight (local time), I'm not as "fresh" as I'd like to be...
Codepad-link

PHP + Regex expressions

Below are 2 pager alert messages and I have a sore head of a time trying to extract the address and job details of the second message into a php string using Regex...
Here are 2 example messages:
0571040 15:45:21 30-04-12 ##ALERT F546356345 THEB8 STRUC1 SMELL OF SMOKE AND ALARM OPERATING 900 SOME ROAD SOMESUBURB /CROSSSTREET1 RD //CROSSTREET2 AV M 99 A1 (429085) CTHEB CBOROS PT28 [THEB]
0571040 15:45:21 30-04-12 ##ALERT F546356345 THEB8 STRUC1 SMELL OF SMOKE AND ALARM OPERATING 4 / 900 SOME ROAD SOMESUBURB /CROSSSTREET1 RD //CROSSTREET2 AV M 99 A1 (429085) CTHEB CBOROS PT28 [THEB]
You will note the second address has 4 / 900 at the start or it could say Unit 4 / 900... and this is where my issue starts! The addresses come in different formats, I have "normal" numbered addresses and "Corner Of" addresses sorted elsewhere but this address one with no 4 at 900 someroad has me stumped. The extra / has screwed my expression up... Help! :)
In my expression I use the first slash as the first cross street but in the second case above the first / is now a part of the address... Below is what I have so far:
function get_string_between2($string, $start, $end){
$string = " ".$string;
$ini = strpos($string,$start);
if ($ini == 0) return "";
$ini += strlen($start);
$len = strpos($string,$end,$ini) - $ini;
return substr($string,$ini,$len);
}
$fullstring = "$rawPage";
if ( strpos($fullstring, ' STRUC1 ')!== false )
{
$parsed = get_string_between2($fullstring, "STRUC1", "/");
}
$input = "$parsed";
preg_match('/([^0-9]+)(.*)/', $input, $matches);
$jobdet = "$matches[1]";
$jobadd = "$matches[2]";
Now this works fine for the top message and I get this as the result:
$jobdet = SMELL OF SMOKE AND ALARM OPERATING
$jobadd = 900 SOME ROAD SOMESUBURB
$firstcrossstreet = /CROSSSTREET1 RD
$secondcrossstreet = //CROSSSTREET2 AV
For the second message it's all wrong with this the result:
$jobdet = SMELL OF SMOKE AND ALARM OPERATING
$jobadd = 4
$firstcrossstreet = / 900 SOME ROAD SOMESUBURB /CROSSSTREET1 RD
$secondcrossstreet = //CROSSSTREET2 AV
I know it's the / causing it but how can I make a expression that handles either case?
With regular expressions, you must escape the forward slash, as these are used as part of the expression. A typical expression looks as follows:
/expression/modifiers
expression is your regex, and modifiers change the execution and result type. eg:
/<[^>]+>/g
This should return all HTML tags in a string. The regex is <[^>]+> and it is between two forward slashes. Therefore you escape the forward slashes - / - to achieve a literal string forward slash.

Categories