我想像
一样修改HTMLI am <b>Sadi, novice</b> programmer.
到
I am <b>Sadi, learner</b> programmer.
要做到这一点,我将使用字符串“新手程序员”进行搜索。我该怎么办?有什么想法吗?
使用多个单词“新手程序员”进行搜索。这可能是一个完整的句子。应忽略额外的空白区域(例如新行,制表符),并且在搜索过程中必须忽略任何标记。但在更换标签期间必须保留。
这是一种转换器。如果不区分大小写会更好。
谢谢
萨迪
更多说明:
我得到了一些可能的解决方案。但如果您有任何想法,请继续发帖。
我想在有人错过的情况下更加澄清问题。主要帖子将问题显示为示例场景。
1)现在问题是在不考虑标签的情况下找到并替换一些字符串。标签可以显示在单个单词中。字符串可能包含多个单词。 标记仅出现在内容字符串或文档中。 搜索词组从不包含任何标记。
我们可以轻松删除所有标签并进行一些文本操作。但是这里出现了另一个问题。
2)标签必须保留,即使在替换文本后也是如此。这就是示例所示。
再次感谢您的帮助
答案 0 :(得分:4)
好吧我认为这就是你想要的。它将您的输入搜索和替换,将它们拆分为由空格分隔的字符串数组,生成一个正则表达式,找到包含任意数量的空格/ html标记的输入句子,并将替换为替换句子,并在单词之间替换相同的标记
如果搜索句子的wordcount高于替换的wordcount,它只使用任何额外单词之间的空格,如果替换wordcount高于搜索,它将在末尾添加所有“孤立”标签。它还处理查找和替换中的正则表达式字符。
<?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>
答案 1 :(得分: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
正则表达式所说的测试版本是这样的:匹配任何字符集直到你达到“新手”并将其放入捕获组,然后匹配以'&lt;'开头的东西。并且在其后面有任意数量的字符,然后以'&gt;'结尾(但是不要捕捉它),但是那时只有匹配的东西与白色空间然后可能再次匹配以'&lt;'开头的东西并且在其后面有任意数量的字符,然后以'&gt;'结尾(但不要捕获它)然后必须由程序员跟随任意数量的字符并将其放入捕获组。
我会做一些特定的测试,因为我可能错过了一些东西。 Regex是程序员最好的朋友!
答案 2 :(得分:1)
嗯,可能有一种更好的方法,但不在我的脑海中(假设标签不会出现在单词的中间,HTML格式正确等等)......
基本上,你需要三件事(对不起,如果这听起来很光顾,不是那样的话): 1.一种忽略标签的子串匹配方法。 2.一种使替换保留标签的方法。 3.把所有这些放在一起的方法。
1 - 这可能是最困难的一点。一种方法是遍历源字符串中的所有字符(字符串基本上是字符数组,因此您可以像访问数组元素一样访问字符),尝试从搜索字符串中匹配尽可能多的字符,停止当你要么匹配所有的字符,要么用完了要匹配的字符。 '&lt;'之间和之间的任何字符和'&gt;'应该被忽略。一些伪代码(检查一下,它已经很晚了,可能会有错误):
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 - 这是一个简单的部分,只需将修改后的部分和其他两个位重新连接在一起。
如果是这样的话,我可能会非常复杂化这种思想。
答案 3 :(得分:0)
除非cOm已经写好了,否则正则表达式将是最佳方式:
$cleaned_string = preg_replace('/\<.\>/', $raw_text, "");
或类似的东西。我需要研究/测试正则表达式。
然后您可以使用简单的 $foobar = str_replace($find, $replace_with, $cleaned_string);
来查找要替换的文本。
没有意识到他想把HTML放进去。这就是所有的正则表达式,而且比我现在知道的还多。
知道我所知道的,技术方面我可能会使用一个表达式,该表达式不会忽略单词之间的空格,而是在<
和>
之间括号,然后使用正则表达式的包含变量的能力输出。
答案 4 :(得分:0)
有趣的问题。
我会使用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 :(得分:0)
由于你没有详细说明你将使用它的内容,我将使用你的例子“我是 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 ='learner';
$ pattern =“/(<.>)?$orig(<.>)?/”;
$ final =“$ 1 $ replace $ 2”;
$ result = htmlspecialchars(preg_replace($ pattern,$ final,$ string));
echo $ result;
唯一的问题是,如果你正在寻找超过一个字的东西。
编辑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