How to replace the first occurrence in a string? - php

I am trying to replace every question mark "?" in a string with a values in an array.
I need to go through a string, and replace the first occurrence of '?' in the string with a value. I would need to do that for every occurrence
Here is what I tried
function sprintf2($str='', array $values = array(), $char = '?')
{
if (!$str){
return '';
}
if (count($values) > 0)
{
foreach ($values as $value)
{
$str = preg_replace('/'. $char . '/', $value, $str, 1);
}
}
echo $str;
}
But I am getting the following exception
preg_replace(): Compilation failed: nothing to repeat at offset 0
The following shows how I am calling the function
$bindings = array(10, 500);
$str = "select * from `survey_interviews` where `survey_id` = ? and `call_id` = ? limit 1";
sprintf2($str, $bindings);
What am I doing wrong here? why do I get this exception?

Use str_replace instead of preg_replace, since you're replacing a literal string, not a regular expression pattern.
However, str_replace always replaces all matches, there's no way to limit it to just the first match (preg_replace is similar). The 4th argument is not a limit, it's a variable that gets set to the number of matches that were found and replaced. To replace just one match, you can combine strpos and substr_replace:
function sprintf2($str='', array $values = array(), $char = '?')
{
if (!$str){
return '';
}
if (count($values) > 0)
{
$len = strlen($char);
foreach ($values as $value)
{
$pos = strpos($str, $char);
if ($pos !== false) {
$str = substr_replace($str, $value, $pos, strlen($char));
}
}
}
echo $str;
}
DEMO

You need to escape the '?' sign in your regexp using a backslash ( '\?' instead of '?').
But your code can be easily refactored to use preg_replace_callback instead:
$params = array(1, 3);
$str = '? bla ?';
echo preg_replace_callback('#\?#', function() use (&$params) {
return array_pop($params);
}, $str);

Hope this code helps you.
function sprintf2($string = '', array $values = array(), $char = '?')
{
if (!$string){
return '';
}
if (count($values) > 0)
{
$exploded = explode($char, $string);
$i = 0;
$string = '';
foreach ($exploded as $segment) {
if( $i < count($values)){
$string .= ($segment . $values[$i]);
++$i;
}
}
}
echo $string;
}
$bindings = array(10, 500);
$str = "select * from `survey_interviews` where `survey_id` = ? and `call_id`= ? limit 1";
echo sprintf2($str, $bindings);
Explanation:
In your code, you're using the preg_match and the first parameter in the preg_match method is a regex pattern. what you're trying to replace is ? has a valid meaning for 0 or 1 CHARACTER. So, you've to escape that by doing \?. Though all the characters are not needed to be escaped, so, for making your method work, you've to check the character that are valid for any regex.
In my code, I've split the string for the character you want. then appending the values at the end of the part what we get from the array. and this should be done till the values length of the value array, otherwise the offset error will occur.

Related

preg_match on formula and characters given?

I need to be able to tell if there is a match of serials given the following:
$formula = 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX';
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$serials = array(
'9876-345-ABC',
'7856Y-YURYW-00UEW-YUI23-YYYYY',
'0934Y-R6834-27495-89999-11123'
);
So, given the following $serials array, how to return true for all values matching any of the characters in $chars using the specified formula, where X is a placeholder for any character inside of $chars. But I also need to make sure the hyphens in the formula are in the right place in the value of the serials given.
foreach($serials as $serial)
{
if(preg_match("???", $serial) === 0)
echo 'found';
}
Should echo found on the last 2 elements of $serials. Seems simple enough, but I still can't wrap my head around regexes no matter how hard I try.
Certainly not the best one, but give it a shot and comment
Assumption :- formula contains only X's
$formula = 'XXX-XX-XXX-X-XXXXX';
$parts = split("\-", $formula);
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$reg = '';
foreach ($parts as $x) {
$reg = $reg . "" . '[' . "" . $chars . "" . ']{' . "" . strlen($x) . "" . "}" . "" . "-";
}
$reg = substr_replace($reg, '', -1);
$serials = array(
'9876-345-ABC',
'7856Y-YUR-00W-YUI23-YYY',
'0934Y-R6834-27495-89999-11123',
'XXX-XX-XXX-X-XXXXX'
);
$reg = '/^' . "" . $reg . "" . '$/';;
foreach($serials as $serial) {
if(preg_match($reg, $serial) != 0) {
echo $serial;
echo "\n";
}
}
Ideone Demo
$formula = 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX';
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$serials = array(
'9876-345-ABC',
'7856Y-YURYW-00UEW-YUI23-YYYYY',
'0934Y-R6834-27495-89999-11123'
);
foreach($serials as $serial) {
$str = str_replace(str_split($chars), 'X', $serial);
echo $str == $formula ? "yes" : "no";
}
You could go for (in multiline mode):
^(?:[0-9A-Z]{3,5}-?){3,5}$
# match the start of the line
# open a non-capturing group (?:
# look for a digit (0-9) or an uppercase letter (A-Z)
# ... between 3-5 times
# make the dash optional -?
# and repeat the non-capturing group 3-5 times
# $ makes sure this is the end of the string
As the wonderful regex101.com does not seem to work at the moment, here a non graphical example of the regex. It will match the ones with an asterisk at the end:
9876-345-ABC *
7856Y-YURYW-00UEW-YUI23-YYYYY *
0934Y-R6834-27495-89999-11123 *
this-one-not
this one neither
Translated to PHP, this would be:
$regex = '~^(?:[0-9A-Z]{3,5}-?){3,5}$~';
if (preg_match($regex, $string)) {
echo "This is a valid serial";
}
You may do it this way, it will use "{}" on successive X.
/**
* $formula = 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX';
* $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
* $serials = array(
* '9876-345-ABC',
* '7856Y-YURYW-00UEW-YUI23-YYYYY',
* '0934Y-R6834-27495-89999-11123'
* );
*/
function checkThisFormula($formula, $chars, array $serials) {
$formulaLength = strlen($formula);
$regex = "";
$charsRegex = "[".$chars."]";
$lastIsX = false;
$nbX = 0;
// let's construct the regex from formula
for($i = 0; $i < $formulaLength; $i++) {
if($formula[$i] === 'X') {
// let's count how many X we see before writing
$nbX++;
$lastIsX = true;
} else {
if($lastIsX) {
// end of successive Xs
$regex .= "[".$chars."]";
if($nbX > 1) {
$regex .= "{".$nbX."}";
}
// reinit X count
$lastIsX = false;
$nbX = 0;
}
// have to be this exact char
$regex .= '\\'.$formula[$i];
}
}
if($lastIsX) {
// if the last char is an X, have to write it too !
$regex .= "[".$chars."]";
if($nbX > 1) {
$regex .= "{".$nbX."}";
}
}
// let's make the regex with flag for case insensitive
$regex = "#".$regex."#i";
$result = array();
// let's loop on every serial to test it
foreach($serials as $serial) {
$result[$serial] = preg_match($regex, $serial);
}
return $result;
}
output :
Array
(
[9876-345-ABC] => 0
[7856Y-YURYW-00UEW-YUI23-YYYYY] => 1
[0934Y-R6834-27495-89999-11123] => 1
)
I think the easy way would do something like that:
foreach($serials as $serial)
{
if(preg_match("/([$chars]{5}-){4}[$chars]{5}/", $serial) == 1)
echo 'found - '.$serial.'<br>';
}
Result would be:
found - 7856Y-YURYW-00UEW-YUI23-YYYYY
found - 0934Y-R6834-27495-89999-11123
I hope that's what you want to do.

Check if substring is contained anywhere in the actual string

I have the following string:
$str = "A string".
When I use:
preg_match("/A string/", $str)
I get the match everything works perfectly fine, but I want to use the same regular expression for another string:
$str2 = "A test string".
For this case I can use:
"/A (test )?string/"
But I want it to be more complex, it should also be able to match strings like this, for example:
A sttest ring
test A string
I mean that the substring "test " can appear anywhere in the subject.
Is it even possible to find a regular expression for this?
You could try something like this
$str = "My test String";
if (strpos($str, 'test') !== false) {
echo 'true';
}
Try this:
$str = "A sttest ring";
$newstr = preg_replace("/\s*test\s*/", "", $str);
preg_match("/A string/", $newstr, $matches);
echo $matches ? 'true' : 'false';
Online Demo
I would create a function for that:
function containsLetters($testString, $lettersString, $caseSensitive = false, $whiteSpaces = false){
if(!$caseSensitive){
$testString = strtolower($testString); $lettersString = strtolower($lettersString);
}
if($whiteSpaces){
$tw = $testString; $lw = $lettersString;
}
else{
$tw = preg_replace('/\s/', '', $testString); $lw = preg_replace('/\s/', '', $lettersString);
}
$tst = str_split($tw); $ltr = str_split($lw); $c = count($ltr); $r = array();
foreach($ltr as $l){
foreach($tst as $i => $t){
if($l === $t){
$a = array_splice($tst, $i, 1); $r[] = $a[0];
}
}
}
if(count($r) >= $c){
return true;
}
return false;
}
$test = containsLetters('A sttest ring', 'a test string');
Just pass true to the 3rd argument to make it case sensitive. Also, pass true to the 4th argument to compare white spaces.

Camelize a php string

How to convert this type of php string to camel case?
$string = primary-getallgroups-sys
I've tried but just found different solutions to camelize a string with spaces. Like ucword($string), but it just capitalize the first word. When i add a delimeter of hyphen (-), it gives error.
$string = 'primary-getallgroups-sys';
// split string by '-'
$words = explode('-', $string);
// make a strings first character uppercase
$words = array_map('ucfirst', $words);
// join array elements with '-'
$string = implode('-', $words);
echo $string; // is now Primary-Getallgroups-Sys
You can make a function to convert these types of string to camel cases.
Try this:
<?php
// CONVERT STRING TO CAMEL CASE SEPARATED BY A DELIMITER
function convertToCamel($str, $delim){
$exploded_str = explode($delim, $str);
$exploded_str_camel = array_map('ucwords', $exploded_str);
return implode($delim, $exploded_str_camel);
}
$string = 'primary-getallgroups-sys';
echo convertToCamel($string, '-'); // Answer
?>
In fact camel case is more like this: iAmCamelCased.
And this is mixed case: IAmMixedCased.
#sundas-mushtaq Also note that hyphens will break your code if used in symbol names (like in functions or variables).
To camelize, use this:
function camelize($word, $delimiter){
$elements = explode($delimiter, $word);
for ($i = 0; $i < count($elements); $i++) {
if (0 == $i) {
$elements[$i] = strtolower($elements[$i]);
} else {
$elements[$i] = strtolower($elements[$i]);
$elements[$i] = ucwords($elements[$i]);
}
}
return implode('', $elements);
}
And to mixify, use this :
function mixify($word, $delimiter){
$word = strtolower($word);
$word = ucwords($word, $delimiter);
return str_replace($delimiter, '', $word);
}
From Symfony
function camelize(string $string): string
{
return lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $string))));
}
// if you want to camelize first word also, remove lcfirst call
function camelize($string) {
$tokens = array_map("ucFirst", explode("_", $string)) ;
return lcfirst(implode("",$tokens));
}

Edit all odd words in string to upper case

I need to edit all odd words to upper case.
Here is sample of imput string:
very long string with many words
Expected output:
VERY long STRING with MANY words
I have this code, but it seams to me, that I can do it in better way.
<?php
$lines = file($_FILES["fname"]["tmp_name"]);
$pattern = "/(\S[\w]*)/";
foreach($lines as $value)
{
$words = NULL;
$fin_str = NULL;
preg_match_all($pattern, $value, $matches);
for($i = 0; $i < count($matches[0]); $i = $i + 2){
$matches[0][$i] = strtoupper($matches[0][$i]);
$fin_str = implode(" ", $matches[0]);
}
echo $fin_str ."<br>";
P.S. I need to use only preg_match function.
Here's a preg_replace_callback example:
<?php
$str = 'very long string with many words';
$newStr = preg_replace_callback('/([^ ]+) +([^ ]+)/',
function($matches) {
return strtoupper($matches[1]) . ' ' . $matches[2];
}, $str);
print $newStr;
// VERY long STRING with MANY words
?>
You only need to match the repeating pattern: /([^ ]+) +([^ ]+)/, a pair of words, then preg_replace_callback recurses over the string until all possible matches are matched and replaced. preg_replace_callback is necessary to call the strtoupper function and pass the captured backreference to it.
Demo
If you have to use regular expressions, this should get you started:
$input = 'very long string with many words';
if (preg_match_all('/\s*(\S+)\s*(\S+)/', $input, $matches)) {
$words = array();
foreach ($matches[1] as $key => $odd) {
$even = isset($matches[2][$key]) ? $matches[2][$key] : null;
$words[] = strtoupper($odd);
if ($even) {
$words[] = $even;
}
}
echo implode(' ', $words);
}
This will output:
VERY long STRING with MANY words
You may don't need regex simply use explode and concatenate the string again:
<?php
function upperizeEvenWords($str){
$out = "";
$arr = explode(' ', $str);
for ($i = 0; $i < count($arr); $i++){
if (!($i%2)){
$out .= strtoupper($arr[$i])." ";
}
else{
$out .= $arr[$i]." ";
}
}
return trim($out);
}
$str = "very long string with many words";
echo upperizeEvenWords($str);
Checkout this DEMO

PHP sentence case function

I have got a function from Google to clean my paragraph in sentence case.
I want to modify this function such that it converts more than 2 new line character to newline. Else it converts all new line characters in space. So I would have it in paragraph format. Here it's converting all new line character to space.
It's converting in proper sentence case. But, In case If I found single word having first word as capital then I need function to ignore that. As Sometimes, if there would be any noun It needs to be capital. We can't change it small case. Else if noun and it is having more than 2 capital characters other than first character than convert it to lower case.
Like -> Noun => Noun but NoUn => noun. Means I want if other than first character is capital than it convert it two lower case Else it keep it in same format.
function sentence_case($string) {
$sentences = preg_split('/([.?!]+)/', $string, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
$new_string = '';
foreach ($sentences as $key => $sentence) {
$new_string .= ($key & 1) == 0?
ucfirst(strtolower(trim($sentence))) :
$sentence.' ';
}
$new_string = preg_replace("/\bi\b/", "I", $new_string);
//$new_string = preg_replace("/\bi\'\b/", "I'", $new_string);
$new_string = clean_spaces($new_string);
$new_string = m_r_e_s($new_string);
return trim($new_string);
}
function sentence_case($str) {
$cap = true;
$ret='';
for($x = 0; $x < strlen($str); $x++){
$letter = substr($str, $x, 1);
if($letter == "." || $letter == "!" || $letter == "?"){
$cap = true;
}elseif($letter != " " && $cap == true){
$letter = strtoupper($letter);
$cap = false;
}
$ret .= $letter;
}
return $ret;
}
This will preserve existing proper noun capitals, acronyms and abbreviations.
This should do it:
function sentence_case($string) {
// Protect the paragraphs and the caps
$parDelim = "/(\n+\r?)/";
$string = preg_replace($parDelim, "#PAR#", $string);
$string = preg_replace("/\b([A-Z])/", "#$1#", $string);
$sentences = preg_split('/([.?!]+)/', $string, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
$new_string = '';
foreach ($sentences as $key => $sentence) {
$new_string .= ($key & 1) == 0?
ucfirst(strtolower(trim($sentence))) :
$sentence.' ';
}
$new_string = preg_replace("/\bi\b/", "I", $new_string);
//$new_string = preg_replace("/\bi\'\b/", "I'", $new_string);
$new_string = clean_spaces($new_string);
$new_string = m_r_e_s($new_string);
// Restore the paragraphs and the caps
$new_string = preg_replace("#PAR#", PHP_EOL, $new_string);
$new_string = preg_replace("/#([A-Z])#/", "$1", $new_string);
return trim($new_string);
}
It works by identifying the items you want to protect (paragraph & first cap) and marking it with a string that you can then replace back when you are done. The assumption is that you don't already have #PAR# and #A-Z# in the string. You can use whatever you want to use for the paragraph delimiter afterwards if you want to force a certain type of line ending or several lines in between.
Slightly modifying Sylverdrag's function, I got something that is working well for me. This version works for every messed-up sentence I've thrown at it so far :)
function sentence_case($string) {
$string = strtolower($string); // ADDED THIS LINE
// Protect the paragraphs and the caps
$parDelim = "/(\n+\r?)/";
$string = preg_replace($parDelim, "#PAR#", $string);
$string = preg_replace("/\b([A-Z])/", "#$1#", $string);
$sentences = preg_split('/([.?!]+)/', $string, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
$new_string = '';
foreach ($sentences as $key => $sentence) {
$new_string .= ($key & 1) == 0?
ucfirst(strtolower(trim($sentence))) :
$sentence.' ';
}
$new_string = preg_replace("/\bi\b/", "I", $new_string);
// GOT RID OF THESE LINES
// $new_string = preg_replace("/\bi\'\b/", "I'", $new_string);
// $new_string = clean_spaces($new_string);
// $new_string = m_r_e_s($new_string);
// Restore the paragraphs and the caps
$new_string = preg_replace("#PAR#", PHP_EOL, $new_string);
$new_string = preg_replace("/#([A-Z])#/", "$1", $new_string);
$new_string = preg_replace("/#([A-Z])#/", "$1", $new_string);
preg_match('/#(.*?)#/', $new_string, $match); // MODIFIED THIS LINE TO USE (.*?)
return trim($new_string);
}
$string = "that IS it!! YOU and i are Totally DONE hanging out toGether... like FOREveR DUDE! i AM serious. a good FRIEND would NOT treAT me LIKE you dO. it Seems, howEVER, THAT you ARE not ONE of tHe GOOD ONEs.";
echo "<b>String 1:</b> " . $string . "<br><b>String 2:</b> " . sentence_case($string);
PHP Sandbox
:) :)

Categories