Сохранить HTMLformat после замены некоторого текста (используя PHP и JS)
Я бы хотел изменить HTML как
I am <b>Sadi, novice</b> programmer.
к
I am <b>Sadi, learner</b> programmer.
Чтобы сделать это, я буду искать с помощью строки " начинающий программист". Как я могу это сделать? Любая идея?
Он использует более одного слова "начинающий программист". Это может быть целая фраза. Дополнительное пустое пространство (например, новая строка, вкладка) следует игнорировать, и во время поиска любой тег следует игнорировать. Но во время замены тег должен быть сохранен.
Это своего рода конвертер. Будет лучше, если он нечувствителен к регистру.
Спасибо
Сади
Дополнительные пояснения:
Я получаю хороший ответ с возможным решением. Но, пожалуйста, продолжайте публикацию, если у вас есть идея.
Я хотел бы уточнить проблему на случай, если кто-нибудь ее пропустит. Основная статья показывает проблему как примерный сценарий.
1) Теперь проблема найти и заменить некоторую строку без учета тегов. Теги могут отображаться в пределах одного слова. Строка может содержать несколько слов. Тег отображается только в строке содержимого или документе. Поисковая фраза никогда не содержит тегов.
Мы можем легко удалить все теги и выполнить некоторую текстовую операцию. Но здесь возникает другая проблема.
2) Теги должны сохраняться, даже после замены текста. Это показывает пример.
Еще раз спасибо за помощь
Ответы
Ответ 1
ok Я думаю, что это то, что вы хотите. он принимает ваш поиск и заменяет ваш вход, разбивает их на массивы строк, разделенных пробелом, генерирует регулярное выражение, которое находит входное предложение с любым количеством тэгов whitespace/html, и заменяет его заменяющим предложением теми же тегами, которые были заменены словами.
если словосочетание поискового предложения выше, чем слово замены, оно просто использует пробелы между любыми дополнительными словами, а если замена wordcount выше, чем поиск, он добавит все теги "осиротевшие" в конец. он также обрабатывает символы regexp в поиске и замене.
<?php
function htmlFriendlySearchAndReplace($find, $replace, $subject) {
$findWords = explode(" ", $find);
$replaceWords = explode(" ", $replace);
$findRegexp = "/";
for ($i = 0; $i < count($findWords); $i++) {
$findRegexp .= preg_replace("/([\\$\\^\\|\\.\\+\\*\\?\\(\\)\\[\\]\\{\\}\\\\\\-])/", "\\\\$1", $findWords[$i]);
if ($i < count($findWords) - 1) {
$findRegexp .= "(\s?(?:<[^>]*>)?\s(?:<[^>]*>)?)";
}
}
$findRegexp .= "/i";
$replaceRegexp = "";
for ($i = 0; $i < count($findWords) || $i < count($replaceWords); $i++) {
if ($i < count($replaceWords)) {
$replaceRegexp .= str_replace("$", "\\$", $replaceWords[$i]);
}
if ($i < count($findWords) - 1) {
$replaceRegexp .= "$" . ($i + 1);
} else {
if ($i < count($replaceWords) - 1) {
$replaceRegexp .= " ";
}
}
}
return preg_replace($findRegexp, $replaceRegexp, $subject);
}
?>
Вот результаты нескольких тестов:
Original : <b>Novice Programmer</b>
Search : Novice Programmer
Replace : Advanced Programmer
Result : <b>Advanced Programmer</b>
Original : Hi, <b>Novice Programmer</b>
Search : Novice Programmer
Replace : Advanced Programmer
Result : Hi, <b>Advanced Programmer</b>
Original : I am not a <b>Novice</b> Programmer
Search : Novice Programmer
Replace : Advanced Programmer
Result : I am not a <b>Advanced</b> Programmer
Original : Novice <b>Programmer</b> in the house
Search : Novice Programmer
Replace : Advanced Programmer
Result : Advanced <b>Programmer</b> in the house
Original : <i>I am not a <b>Novice</b> Programmer</i>
Search : Novice Programmer
Replace : Advanced Programmer
Result : <i>I am not a <b>Advanced</b> Programmer</i>
Original : I am not a <b><i>Novice</i> Programmer</b> any more
Search : Novice Programmer
Replace : Advanced Programmer
Result : I am not a <b><i>Advanced</i> Programmer</b> any more
Original : I am not a <b><i>Novice</i></b> Programmer any more
Search : Novice Programmer
Replace : Advanced Programmer
Result : I am not a <b><i>Advanced</i></b> Programmer any more
Original : I am not a Novice<b> <i> </i></b> Programmer any more
Search : Novice Programmer
Replace : Advanced Programmer
Result : I am not a Advanced<b> <i> </i></b> Programmer any more
Original : I am not a Novice <b><i> </i></b> Programmer any more
Search : Novice Programmer
Replace : Advanced Programmer
Result : I am not a Advanced <b><i> </i></b> Programmer any more
Original : <i>I am a <b>Novice</b> Programmer</i> too, now
Search : Novice Programmer too
Replace : Advanced Programmer
Result : <i>I am a <b>Advanced</b> Programmer</i> , now
Original : <i>I am a <b>Novice</b> Programmer</i>, now
Search : Novice Programmer
Replace : Advanced Programmer Too
Result : <i>I am a <b>Advanced</b> Programmer Too</i>, now
Original : <i>I make <b>No money</b>, now</i>
Search : No money
Replace : Mucho$1 Dollar$
Result : <i>I make <b>Mucho$1 Dollar$</b>, now</i>
Original : <i>I like regexp, you can do [A-Z]</i>
Search : [A-Z]
Replace : [Z-A]
Result : <i>I like regexp, you can do [Z-A]</i>
Ответ 2
Я бы сделал это:
if (preg_match('/(.*)novice((?:<.*>)?\s(?:<.*>)?programmer.*)/',$inString,$attributes) {
$inString = $attributes[1].'learner'.$attributes[2];
}
Он должен соответствовать любому из следующих элементов:
novice programmer
novice</b> programmer
novice </b>programmer
novice<span> programmer
Тест-версия того, что состояния регулярных выражений будет выглядеть примерно так: сопоставьте любой набор символов, пока не достигнете "новичка" и не поместите его в группу захвата, а затем, возможно, сопоставьте что-то, что начинается с "<" и имеет любое количество символов после него, а затем заканчивается на " > " (но не фиксирует его), но тогда там только что-то сопоставляется с пробелом, а затем, возможно, снова совпадает с чем-то, начинающимся с символа '<' и имеет любое количество символов после него, а затем заканчивается на " > " (но не записывает его), за которым следует следовать программист, за которым следует любое количество символов и помещает его в группу захвата.
Я бы сделал некоторые конкретные тесты, хотя, возможно, я пропустил некоторые вещи. Regex - лучший друг программистов!
Ответ 3
Ну, может быть, лучший способ, но с моей головы (предполагая, что теги не будут отображаться в середине слов, HTML хорошо сформирован и т.д.)...
По сути, вам понадобятся три вещи (извините, если это звучит покровительствовать, а не так)
1. Метод подстроки, который игнорирует теги.
2. Способ сделать замену сохранением тегов.
3. Способ соединить все это.
1 - Это, наверное, самый сложный бит. Один из методов - перебрать все символы в исходной строке (строки - это в основном массивы символов, чтобы вы могли обращаться к символам так, как если бы они были элементами массива), пытаясь сопоставить как можно больше символов из строки поиска, останавливаясь когда вы либо сопоставляете все символы, либо заканчиваете символы, чтобы они совпадали. Любые символы между и включая '<' и ' > ' следует игнорировать. Некоторые псевдокоды (проверьте это, поздно, и могут быть ошибки):
findMatch(startingPos : integer, subject : string, searchString : string){
//Variables for keeping track of characters matched, positions, etc.
inTag = false;
matchFound = false;
matchedCharacters = 0;
matchStart = 0;
matchEnd = 0;
for(i from startingPos to length(searchString)){
//Work out when entering or exiting tags, ignore tag contents
if(subject[i] == '<' || subject[i] == '>'){
inTag = !inTag;
}
else if(!inTag){
//Check if the character matches expected in search string
if(subject[i] == searchString[matchedCharacters]){
if(!matchFound){
matchFound = true;
matchStart = i;
}
matchedCharacters++;
//If all of the characters have been matched, return the start and end positions of the substring
if(matchedCharacters + 1 == length(searchString)){
matchEnd = i - matchStart;
return matchStart, matchEnd;
}
}
else{
//Reset counts if not found
matchFound = false;
matchCharacters = 0;
}
}
}
//If no full matches were found, return error
return -1;
}
2 - Разделите исходный код HTML на три строки - бит, с которым вы хотите работать (между двумя позициями, возвращаемыми функцией сопоставления) и частью до и после. Разделите бит, который хотите изменить, например:
$parts = preg_split("/(<[^>]*>)/",$string, -1, PREG_SPLIT_DELIM_CAPTURE);
Храните записи о том, где находятся теги, объединяйте сегменты без тегов и выполняйте замену подстроки на этом как обычно, затем снова разделяйте измененную строку и снова создавайте теги на месте.
3 - Это легкая часть, просто конкатенируйте измененную часть и два других бита назад.
Возможно, я ужасно усложнил этот ум, если так просто игнорировать меня.
Ответ 4
Если cOm уже написано, регулярное выражение будет лучшим способом:
$cleaned_string = preg_replace('/\<.\>/', $raw_text, "");
Или что-то в этом роде. Мне нужно было бы изучить/протестировать регулярное выражение.
Затем вы можете просто использовать простой $foobar = str_replace($find, $replace_with, $cleaned_string);
для поиска текста, который хотите заменить.
Не понял, что он хочет вернуть HTML. Это все регулярное выражение для этого и больше, чем я знаю на данный момент.
Зная, что я знаю, по методике, я бы, вероятно, использовал выражение, которое не игнорировало пробел между словами, но делало между скобками <
и >
, а затем использовало возможности, связанные с переменными regex для вывода.
Ответ 5
Интересная проблема.
Я бы использовал DOM и XPath для поиска ближайших узлов, содержащих этот текст, а затем использовал подстроку, чтобы узнать, какой бит строки находится в node. Это будет включать совпадение символов для каждого символа и возможный откат.
Вот первая часть, находящая узлы контейнера:
<?php
error_reporting(E_ALL);
header('Content-Type: text/plain; charset=UTF-8');
$doc = new DOMDocument();
$doc->loadHTML(<<<EOD
<p>
<span>
<i>
I am <b>Sadi, novice</b> programmer.
</i>
</span>
</p>
<ul>
<li>
<div>
I am <em>Cornholio, novice</em> programmer of television shows.
</div>
</li>
</ul>
EOD
);
$xpath = new DOMXPath($doc);
// First, get a list of all nodes containing the text anywhere in their tree.
$nodeList = $xpath->evaluate('//*[contains(string(.), "programmer")]');
$deepestNodes = array();
// Now only keep the deepest nodes, because the XPath query will also return HTML, BODY, ...
foreach ($nodeList as $node) {
$deepestNodes[] = $node;
$ancestor = $node;
while (($ancestor = $ancestor->parentNode) && ($ancestor instanceof DOMElement)) {
$deepestNodes = array_filter($deepestNodes, function ($existingNode) use ($ancestor) {
return ($ancestor !== $existingNode);
});
}
}
foreach ($deepestNodes as $node) {
var_dump($node->tagName);
}
Я надеюсь, что это поможет вам.
Ответ 6
Поскольку вы не указали точную специфика того, для чего вы будете использовать это, я буду использовать ваш пример "Я - сади, новичок программист".
$before = 'I am <b>sadi, novice</b> programmer';
$after = preg_replace ('/I am (<.*>)?(.*), novice(<.*>)? programmer/','/I am $1$2, learner$3 programmer/',$string);
В качестве альтернативы для любого текста:
$string = '<b>Hello</b>, world!';
$orig = 'Hello';
$replace = 'Goodbye';
$pattern = "/(<.*>)?$orig(<.*>)?/";
$final = "/$1$replace$2/";
$result = preg_replace($pattern,$final,$string);
//$result should now be 'Goodbye, world!'
Надеюсь, что это помогло.: Д
Изменить: пример вашего примера со вторым фрагментом кода: $ string = 'Я сади, новичок программист.';
$ orig = 'новичок';
$ replace = 'ученик';
$ pattern = "/(<. > )?$orig(<. > )?/";
$ final = "$ 1 $заменить $2";
$ result = htmlspecialchars (preg_replace ($ pattern, $final, $string)),
echo $result;
Единственная проблема заключается в том, что вы искали нечто большее, чем слово.
Edit 2: Наконец, появился способ сделать это через несколько слов. Здесь код:
function htmlreplace($string,$orig,$replace)
{
$orig = explode(' ',$orig);
$replace = explode(' ',$replace);
$result = $string;
while (count($orig)>0)
{
$shift = array_shift($orig);
$rshift = array_shift($replace);
$pattern = "/$shift\s?(<.*>)?/";
$replacement = "$rshift$1";
$result = preg_replace($pattern,$replacement,$result);
}
$result .= implode(' ',$replace);
return $result;
}
Удачи!: Д