替换一些文本后保留HTML格式(使用PHP和JS)

时间:2010-04-01 21:05:07

标签: php javascript html string replace

我想像

一样修改HTML
I am <b>Sadi, novice</b> programmer.

I am <b>Sadi, learner</b> programmer.

要做到这一点,我将使用字符串“新手程序员”进行搜索。我该怎么办?有什么想法吗?

使用多个单词“新手程序员”进行搜索。这可能是一个完整的句子。应忽略额外的空白区域(例如新行,制表符),并且在搜索过程中必须忽略任何标记。但在更换标签期间必须保留。

这是一种转换器。如果不区分大小写会更好。

谢谢

萨迪


更多说明:

我得到了一些可能的解决方案。但如果您有任何想法,请继续发帖。

我想在有人错过的情况下更加澄清问题。主要帖子将问题显示为示例场景。

1)现在问题是在不考虑标签的情况下找到并替换一些字符串。标签可以显示在单个单词中。字符串可能包含多个单词。 标记仅出现在内容字符串或文档中。 搜索词组从不包含任何标记

我们可以轻松删除所有标签并进行一些文本操作。但是这里出现了另一个问题。

2)标签必须保留,即使在替换文本后也是如此。这就是示例所示。

再次感谢您的帮助

6 个答案:

答案 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