替换一些文本后保留 HTML 格式(使用 PHP 和 JS)
Posted
技术标签:
【中文标题】替换一些文本后保留 HTML 格式(使用 PHP 和 JS)【英文标题】:keep HTMLformat after replace some text (using PHP and JS) 【发布时间】:2011-02-03 13:21:03 【问题描述】:我想修改 html 之类的
I am <b>Sadi, novice</b> programmer.
到
I am <b>Sadi, learner</b> programmer.
为此,我将使用字符串“新手程序员”进行搜索。请问我该怎么做?有什么想法吗?
它使用多个单词“新手程序员”进行搜索。这可能是一个完整的句子。额外的空格(例如新行、制表符)应该被忽略,并且在搜索过程中必须忽略任何标签。但在更换期间必须保留标签。
它是一种转换器。如果它不区分大小写会更好。
谢谢
萨迪
更多说明:
我得到了一些不错的答复,并提供了可能的解决方案。但是,如果您有任何想法,请继续发布。
我想进一步澄清这个问题,以防有人错过。主帖将问题作为示例场景展示。
1) 现在的问题是在不考虑标签的情况下查找和替换一些字符串。标签可能会出现在一个单词中。字符串可能包含多个单词。 标签只出现在内容字符串或文档中。 搜索词组从不包含任何标签。
我们可以很方便的去掉所有的标签,做一些文字操作。但是这里出现了另一个问题。
2) 标记必须保留,即使在替换文本之后也是如此。这就是示例所显示的内容。
再次感谢您的帮助
【问题讨论】:
+1 对此技术感兴趣.... 这可能比看起来更复杂。如果我想用“学习编程”来代替“新手程序员”怎么办?代码应该如何确定将哪些单词放在元素内以及放在元素之后? 就像 matti 说的,基本上你在你的例子中所做的就是用“学习者”替换“新手”,这可以通过一个简单的 str_replace 来实现。如果我们正在讨论用包含更多单词的字符串替换“新手程序员”,那么您可能会遇到问题,您可能无法通过代码解决。 你总是可以用代码解决它,你只需要知道你的规则是什么。正则表达式很棒,可以做很多很棒的事情。 @Matti Virkkunen:你是对的。你明白了:)。这是主要问题:(它不能总是通过简单地替换单词来完成。 【参考方案1】:除非 cOm 已经写好了,否则正则表达式是最好的选择:
$cleaned_string = preg_replace('/\<.\>/', $raw_text, "");
或者类似的东西。我需要研究/测试正则表达式。
然后你可以用一个简单的$foobar = str_replace($find, $replace_with, $cleaned_string);
来找到你要替换的文本。
没有意识到他想把 HTML 放回去。这都是正则表达式,而且比我目前知道的要多。
知道我所知道的,从技术上讲,我可能会使用一个表达式,它不会忽略单词之间之间的空格,而是在<
和>
括号之间忽略空格,然后使用正则表达式输出的包含变量的能力。
【讨论】:
这将完全删除 HTML 格式,并且该帖子专门关于 保持 HTML 格式。 是的,刚刚注意到。很抱歉混淆了。【参考方案2】:嗯,可能有更好的方法,但我没想到(假设标签不会出现在单词中间,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 - 这是简单的部分,只需将修改后的部分和其他两个位连接在一起。
我的想法可能过于复杂了,如果是这样,请忽略我。
【讨论】:
当我第一次遇到这个问题时,我把它分成了两个问题。 1)查找字符串并忽略标签& 2)在保留标签的同时替换字符串。剥离所有标签并替换很容易,但是当我必须保留标签时会出现问题。我需要一点时间来看看你的解决方案。【参考方案3】:我会这样做:
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
正则表达式状态的测试版本类似于:匹配任何字符集,直到达到“新手”并将其放入捕获组,然后可能匹配以“' 结尾(但不要捕获它),但随后只匹配带有空格的内容,然后可能再次匹配以 '' 结尾(但不要捕获它),然后程序员必须跟在后面跟着任意数量的字符并将其放入捕获组中。
我会做一些具体的测试,因为我可能错过了一些东西。正则表达式是程序员最好的朋友!
【讨论】:
这是非常硬编码的,但可能是一个可能的解决方案,谢谢 还有一件事,新手也被替换了,只是你看不到效果,因为两个词(搜索替换)都是“新手”。 不,它不是preg_replace
...它是preg_match
,它只会在模式匹配并且捕获组被移动到$attributes然后重新组合到所需的情况下才会触发细绳。就硬编码而言,它是为您提供所需的内容,但正则表达式可以适应您真正需要的任何内容。
"我是Sadi,新手程序员。我很简单。我是Sadi,新手程序员。我很简单" -- 无法正常工作使用此字符串,此处的结果会出现两次。我尝试过使用 preg_match_all 和 preg_match。而且它永远不会取代程序员。它保持原样。请问有什么想法吗?【参考方案4】:
有趣的问题。
我会使用 DOM 和 XPath 来查找包含该文本的最近节点,然后使用子字符串匹配来找出字符串的哪个位在哪个节点中。不过,这将涉及到每个字符的匹配和可能的回溯。
这是第一部分,寻找容器节点:
<?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);
希望对你有所帮助。
【讨论】:
“不过,这将涉及到每个字符的匹配和可能的回溯。”虽然听起来不错,但它可能不是生产环境的好解决方案。但我会看看你的解决方案。谢谢【参考方案5】:由于您没有具体说明您将使用它的具体用途,我将使用您的示例“我是 sadi,新手 程序员”。
$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!'
希望对您有所帮助。 :d
编辑:您的示例的示例,第二段代码: $string = '我是sadi,新手程序员。'; $orig = '新手'; $replace = '学习者'; $pattern = "/(<.>>)?$orig(<.>>)?/"; $final = "$1$replace$2"; $result = htmlspecialchars(preg_replace($pattern,$final,$string)); 回显$结果;
唯一的问题是,如果您要搜索的内容超过一个单词。
编辑 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;
玩得开心! :d
【讨论】:
请看例子。它使用多个单词“新手程序员”进行搜索。这可能是一个完整的句子。搜索过程中应忽略多余的空格(例如换行符、制表符)和任何标记。 嗯,我认为它没有考虑空格...另一个修复即将到来,请等待几分钟。 无法正常工作。它的工作原理就像用单词替换。即使用单词替换也不总是有效。示例: $inString = '我是 Sadi,新手 程序员。我很简单。我是 Sadi,新手 程序员。我是简单的程序员'; echo htmlreplace($inString, '新手程序员', '蹩脚的开发者');结果:我是 Sadi,蹩脚的开发人员。我很简单。我是 Sadi,新手开发者。我是简单的开发者【参考方案6】:好的,我认为这就是您想要的。它接受您的输入搜索和替换,将它们拆分为由空格分隔的字符串数组,生成一个正则表达式,查找具有任意数量的空白/html标签的输入句子,并将其替换为替换句子,并在单词之间替换相同的标签.
如果搜索语句的字数高于替换的,它只是在任何多余的单词之间使用空格,如果替换的字数高于搜索,它会在末尾添加所有“孤儿”标签。它还处理查找和替换中的正则表达式字符。
<?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>
【讨论】:
我喜欢这个解决方案。但这里有一个小错误。 $inString = '我是Sadi,新手程序员。我很简单。我是 Sadi,新手 程序员。我很简单'; echo htmlFriendlySearchAndReplace('新手程序员', '蹩脚的开发者', $inString);结果是:我是 Sadi,蹩脚的程序员。我很简单。我是 Sadi,新手开发者。我很简单 抱歉,已编辑答案以修复。更改此行:$findRegexp .= "(\s?(?:<[^>]*>)?\s(?:<[^>]*>)?)";
谢谢,现在它工作得很好。唯一的问题是如果在单词中间找到标签,它就无法工作。例如新手ce 当然,这很难解决,因为我们不能轻易确定标签的位置。如果可以,请发布它的解决方案。
您可以向前或向后移动标签 :) 非常感谢您的解决方案。我尝试过类似的解决方案(作为你的函数)但失败了,因为我对正则表达式不好:(
呃!!!我无法接受答案 :( 接受按钮不见了 :( 可能是因为赏金......但这是最好的解决方案以上是关于替换一些文本后保留 HTML 格式(使用 PHP 和 JS)的主要内容,如果未能解决你的问题,请参考以下文章