сохраните HTMLформат после замены некоторого текста (с использованием PHP и JS)


Я хотел бы изменить HTML, как

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

До

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

Для этого я буду искать с помощью строки " начинающий программист". Как я могу это сделать, пожалуйста? Есть идеи?

Для поиска используется более одного слова "начинающий программист". Это может быть целое предложение. Лишние пробелы (например, новая строка, вкладка) следует игнорировать, и любой тег должен игнорироваться во время поиска. Но во время замены бирка должна быть сохранена.

Это своего рода преобразователь. Так будет лучше если это без учета регистра.

Спасибо

Сади


Больше разъяснений:

Я получаю хороший ответ с возможным решением. Но, пожалуйста, продолжайте публиковать, если у вас есть какие-либо идеи.

Я хотел бы подробнее прояснить проблему на случай, если кто-то ее пропустил. Основной пост показывает проблему в качестве примера сценария.

1) Теперь проблема в том, чтобы найти и заменить некоторую строку без учета тегов . Теги могут отображаться внутри одно-единственное слово. Строка может содержать несколько слов. Тег отображается только в строке содержимого или документе . Поисковая фраза никогда не содержит никаких тегов.

Мы можем легко удалить все теги и выполнить некоторые текстовые операции. Но здесь возникает другая проблема.

2) Теги должны быть сохранены, даже после замены текста. Вот что показывает этот пример.

Еще раз спасибо за помощь

Author: Sadi, 2010-04-02

6 answers

Хорошо, я думаю, это то, чего ты хочешь. он выполняет поиск и замену входных данных, разбивает их на массивы строк, разделенных пробелами, генерирует регулярное выражение, которое находит входное предложение с любым количеством пробелов/тегов 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>
 4
Author: oedo, 2010-04-21 07:21:20

Я бы сделал это:

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

Тестовая версия состояний регулярного выражения будет выглядеть примерно так: сопоставьте любой набор символов, пока не достигнете "новичка" и не поместите его в группу захвата, затем, возможно, сопоставьте что-то, что начинается с "" (но не захватывайте его), но затем сопоставьте что-то только с пробелом, а затем, возможно, снова сопоставьте что-то, что начинается с "" (но не захватывайте его), но затем сопоставьте что-то только с пробелом, а затем, возможно, снова сопоставьте что-то, что начинается с "" (но не захватывайте его), за которым должен следовать программист, за которым следует любое количество символов, и поместите его в группу захвата.

Я бы провел некоторое конкретное тестирование, хотя, возможно, я что-то пропустил. Регулярное выражение - лучший друг программистов!

 3
Author: Kitson, 2010-04-02 11:26:36

Ну, может быть, есть лучший способ, но с моей точки зрения (предполагая, что теги не будут отображаться в середине слов, 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
Author: Moonshield, 2010-04-01 22:39:24

Если COM еще не написал это, регулярное выражение было бы лучшим способом:

$cleaned_string = preg_replace('/\<.\>/', $raw_text, "");

Или что-то в этом роде. Мне нужно было бы исследовать/протестировать регулярное выражение.

Затем вы можете просто использовать простой $foobar = str_replace($find, $replace_with, $cleaned_string);, чтобы найти текст, который вы хотите заменить.

Не понял, что он хотел вернуть HTML обратно. Это все регулярное выражение для этого, и больше, чем я знаю на данный момент.

Зная то, что я знаю, с технической точки зрения я бы, вероятно, использовал выражение, которое не игнорировало пробелы между словами , но между скобками < и >, затем используйте возможности регулярного выражения, содержащие переменные, для вывода.

 0
Author: dclowd9901, 2010-04-01 21:24:44

Интересная проблема.

Я бы использовал 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);
}

Я надеюсь, что это поможет вам в этом.

 0
Author: janmoesen, 2010-04-02 12:43:52

Поскольку вы не дали точной информации о том, для чего вы будете это использовать, я воспользуюсь вашим примером "Я сади, начинающий программист".

$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 = 'Я сади, начинающий программист.';
$orig = 'новичок';
$заменить = 'учащийся';
$шаблон = "/(<.>>)?$ ориг(<.>>)?/";
$final = "$1$заменить $2";
$результат = htmlspecialchars(preg_replace($шаблон,$финал,$строка));
echo $результат;

Единственная проблема заключается в том, что вы искали что-то длиннее слова.

Правка 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

 0
Author: Hussain, 2010-04-16 02:47:37