简体   繁体   中英

Having trouble replacing a value in a tag when a string variable starts with digit

I have some code where, if the value of $subtitle1 contains only letters or spaces, a regex replace works fine. When $subtitle1 string begins with a digit (for example "3rd Edition") the preg_replace function works unexpectedly. If I add a space in the replacement string, then the value of $subtitle1 can start with a digit and its ok, but it puts an unwanted space before the 3 in "3rd Edition".

$raw_xml    = '<property name="subtitle1" type="String">Linux is more than a shell</property>';
$subtitle1  = '3rd Edition';

$replacers  = array (
    '/(<property name="subtitle1" type="String">)([1-9A-Za-z ]+)(<\/property>)/'  => sprintf("$1%s$3",$subtitle1), //1
    '/(<property name="subtitle1" type="String">)([1-9A-Za-z ]+)(<\/property>)/'  => sprintf("$1 %s$3",$subtitle1), //2
    '/(<property name="subtitle1" type="String">)([1-9A-Za-z ]+)(<\/property>)/'  => sprintf("$1%s$3",$subtitle1), //3
);
echo preg_replace(array_keys($replacers), array_values($replacers), $raw_xml);        

//1 (when $subtitle1 = 'Third Edition', outputs: <property name="subtitle1" type="String">Third Edition</property>)
//2 (when $subtitle1 = '3rd Edition', outputs: <property name="subtitle1" type="String"> 3rd Edition</property>)
//3 (when $subtitle1 = '3rd Edition', outputs: rd Edition</property>)

Is there something I can do differently to get this to work the same provided that the type of $subtitle1 var is always a string? I've tried modifiers s, U, but did not get any further. Thanks for any insight into this.

On a purely theoretical plane your code does not work cause the parser search for the backreferences $1 and $3 as variables before the string is evaluated by the sprintf or the pcre regex engine.

So to make it works simply replace the sprintf literal string section:

sprintf("$1%s$3",$subtitle1) -> sprintf('${1}%s${3}',$subtitle1)
# Note the change of $1 -> ${1} to clearly delimit the backreference
# and the use of single quote string '...' instead of  "..." 
# (inside double quotes any $ start an evaluation as variables of string beside)

But for a reliable solution avoid to parse xml with regex and use a specialized (simple and powerful) parser like this:

<?php
$xml = <<<XML
<properties> <!-- Added -->
    <property name="subtitle1" type="String">Linux is more than a shell</property>
</properties>
XML;

$properties = new SimpleXMLElement($xml);
$properties->property[0] = '3rd Edition';

echo $properties->asXML(); //Only the first is changed

See more on the Official Docs .

The problem is because: sprintf("$1%s$3",$subtitle1)

output: $13rd Edition$3 .

I guess that the regex engine understand it as the 13th capturing group.

The good new is, I found a solution for you.

Replace: $subtitle1 = '3rd Edition' ;

By: $subtitle1 = '>3rd Edition<';

and extract the <> from your first and third capturing group just like this.

$replacers  = array (
    '/(<property name="subtitle1" type="String")>([1-9A-Za-z ]+)<(\/property>)/'  => sprintf("$1%s$3",$subtitle1), //1
    '/(<property name="subtitle1" type="String")>([1-9A-Za-z ]+)<(\/property>)/'  => sprintf("$1 %s$3",$subtitle1), //2
    '/(<property name="subtitle1" type="String")>([1-9A-Za-z ]+)<(\/property>)/'  => sprintf("$1%s$3",$subtitle1), //3
);

You can test it here: http://sandbox.onlinephpfunctions.com/code/05bf9a209bdcd6622bf494dc7f4887660e7a93a0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM