Разбор обратной косой черты с помощью str_replace

Скажи у меня есть функция templateMap который для каждого подмассива $array, заменяет каждое вхождение @n (для некоторых n) в данной $string со значениями из этого подмассива, возвращая новый массив подмассивов. Также скажите, что я хочу разрешить пользователю обратную косую черту @ символ (что означает \ быть убитым тоже).

Например:

function templateMap ($string, $array) {
$newArray = array();
foreach($array as $subArray) {
foreach($subArray as $replacements) {
...
}
}
return $newArray;
}// for grouping mysql statements with parentheses
templateMap("(@)", array(" col1 < 5 && col2 > 6 ", " col3 < 3 || col4 > 7"));

Это произведет

array("( col1 < 5 && col2 > 6 )", "( col3 < 3 || col4 > 7 )")

Вот более сложный пример с несколькими аргументами — возможно, нелегко реализовать

templateMap("You can tweet @0 \@2 @1", array(
array("Sarah", "ssarahtweetzz"),
array("John", "jjohnsthetweetiest"),
...
));

/* output:
array(
"You can tweet Sarah @2 ssarahtweetzz",
"You can tweet John @2 jjohnsthetweetiest")
*/

Есть ли способ сделать это с помощью серии str_replace звонки? (В отличие от регулярного выражения или простого конечного автомата.)

Одна вещь, о которой я думал, состояла в том, чтобы заменить случаи \@ с какой-то экзотической строкой, не найденной в текущей строке, такой как zzzzzz, но, конечно, тогда вы должны проверить, находится ли строка в данной строке, и соответственно изменить ее.

-1

Решение

Я думаю, что главная проблема, когда ограничивается только использованием str_replace является то, что у вас мало контроля над тем, какие строки были заменены (так как все вхождения заменяются сразу), и вам нужно быть особенно осторожным при выборе заполнителя для \@ escape-последовательность Существует вероятность того, что два введенных значения вместе сгенерируют строку-заполнитель и, следовательно, будут превращены в @ символ, когда замена заполнителя отменяется.

Ниже приведено грубое решение, которое пытается справиться с этим. Он проверяет по одному заполнителю за раз по строке шаблона, значениям замены и окончательной строке, проверяя, что заполнитель не появляется ни в одной из этих строк и что количество заполнителей, первоначально введенных для \@ соответствует количеству возвращенных заполнителей. Вы, вероятно, хотели бы установить заполнитель по умолчанию вместо xyz (например, нулевой символ или что-то в этом роде), который лучше всего работает для вас, чтобы избежать ненужной обработки.

Это можно вызвать с обоими видами шаблонов замещения (@ а также @<n>) но в настоящее время они не могут быть смешаны.

Это не самый красивый код, который я когда-либо писал, но учитывая str_replace Тем не менее, это мой выстрел, и я надеюсь, что это может помочь вам.

function templateMap ($string, $array, $defaultPlaceholder = "xyz")
{
$newArray = array();

// Create an array of the subject string and replacement arrays
$knownStrings = array($string);
foreach ($array as $subArray) {
if (is_array($subArray)) {
$knownStrings = array_merge($knownStrings, array_values($subArray));
}
else {
$knownStrings[] = $subArray;
}
}

$placeHolder = '';

while (true) {
if (!$placeHolder) {
// This is the first try, so let's try the default placeholder
$placeHolder = $defaultPlaceholder;
}
else {
// We've been here before - we need to try another placeholder
$placeHolder = uniqid('bs-placeholder-', true);
}

// Try to find a placeholder that does not appear in any of the strings
foreach ($knownStrings as $knownString) {
// Does $placeHolder exist in $knownString?
str_replace($placeHolder, 'whatever', $knownString, $count);
if ($count > 0) {
// Placeholder candidate was found in one of the strings
continue 2; // Start over
}
}

// Will go for placeholder "$placeHolder"foreach ($array as $subArray) {
$newString = $string;

// Apply placeholder for \@ - remember number of replacements
$newString = str_replace(
'\@', $placeHolder, $newString, $numberOfFirstReplacements
);

if (is_array($subArray)) {
// Make substitution on @<n>
for ($i = 0; $i <= 9; $i++) {
@$newString = str_replace("@$i", $subArray[$i], $newString);
}
}
else {
// Make substitution on @
@$newString = str_replace("@", $subArray, $newString);
}

// Revert placeholder for \@ - remember number of replacements
$newString = str_replace(
$placeHolder, '@', $newString, $numberOfSecondReplacements
);

if ($numberOfFirstReplacements != $numberOfSecondReplacements) {
// Darn - value substitution caused used placeholder to appear,
// ruining our day - we need some other placeholder
$newArray = array();
continue 2;
}

// Looks promising
$newArray[] = $newString;
}

// All is well that ends well
break;
}
return $newArray;
}

$a = templateMap(
"(@ and one escaped \@)",
array(" col1 < 5 && col2 > 6", " col3 < 3 || col4 > 7")
);
print_r($a);

$a = templateMap(
"You can tweet @0 \@2 @1",
array(
array("Sarah", "ssarahtweetz"),
array("John", "jjohnsthetweetiest"),
)
);
print_r($a);

Выход:

Array
(
[0] => ( col1 < 5 && col2 > 6 and one escaped @)
[1] => ( col3 < 3 || col4 > 7 and one escaped @)
)
Array
(
[0] => You can tweet Sarah @2 ssarahtweetz
[1] => You can tweet John @2 jjohnsthetweetiest
)
1

Другие решения

При замене не может быть никаких @кроме тех, которые нуждаются в замене … поэтому мы должны избавиться от всех \@ последовательности.
Но когда мы избавимся от всех \@ последовательности, не может быть \@ которые на самом деле являются частью \\@ (две обратные косые черты, сопровождаемые @Последовательность
Чтобы избавиться от \\ последовательности, мы можем использовать новый escape-символ %,

В частности, если мы сбежим % как %%тогда мы можем избежать любой другой последовательности как ?%? где ? любой персонаж и будет гарантировано, что ?%? можно избежать, так как % никогда не появится один в середине.

// wrapper for native strings to make chaining easier
class String {
private $str;
public function __construct ($str) {
$this->str = $str;
}
public function replace ($search, $substitute) {
return new self(str_replace($search, $substitute, $this->str));
}
public function toRaw () {
return $this->str;
}
}

function templateMap ($str, $arr) {
$encodedStr = (new String($str))->replace('%', '%%')
->replace('\\\\', '?%?')->replace('\@', '!%!');
$newArr = array();
foreach($arr as $el) {
$encodedStrPieces = explode("@", $encodedStr->toRaw());
foreach($encodedStrPieces as $i => $piece) {
$encodedStrPieces[$i] = (new String($piece))->replace("@", $el)
->replace('!%!', '@')->replace('?%?', '\\')
->replace('%%', '%')->toRaw();
}
$newArr[] = implode($el, $encodedStrPieces);
}
return $newArr;
}$arr = templateMap("(@\@)", array("hello", "goodbye"));
var_dump($arr); // => ["(hello@)", "(goodbye@)"]
1

По вопросам рекламы [email protected]