php
  • regex
  • string
  • replace
  • preg-replace
  • 2013-04-30 31 views 9 likes 
    9

    該更換的每一個字符是我與元素

    $str = 'Just a <span class="green">little</span> -text åäö width 123#'; 
    

    這就是我需要的跨度和空間

    結果,可能是換行也是如此。

    $result = '<span></span><span></span><span></span><span></span> <span></span> <span class="green"><span></span><span></span><span></span><span></span><span></span><span></span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span>'; 
    

    你可能想知道我可能需要這樣做。我想要建立一個角色由塊代表的東西。在Windows XP上看起來有點像碎片整理。

    問題

    • <span></span>替換每個字符。
    • 請勿觸摸字符串中已存在的HTML範圍(可能很難?)。可以有多個HTML元素。
    • 請勿觸摸空格和換行符。
    • 正則表達式應該這樣做嗎?或Xpath?

    到目前爲止我做了什麼?

    我發現對正則表達式的文章而不更換每個字符(摘錄空間和換行符)

    $result = preg_replace("/???/", "<span></span>", $str); 
    print_r($result); 
    
    +0

    試'的preg_replace(「/([^:空間:\ n「)/」,「」,$ str);'[]是一組字符,^是NOT,:空格或\ s是空格\ n是換行符 – Waygood 2013-04-30 10:13:26

    +2

    「不要碰已存在於字符串「部分的HTML是正則表達式解決方案導致問題的地方。你真的想使用DOM解析器,只遍歷文本節點,並在這些節點上應用'/ \ S /' - >'替換。 [這是一個很好的概述你的DOM解析選項](http://stackoverflow.com/questions/3577641/how-to-parse-and-process-html-xml) – 2013-04-30 10:36:59

    +0

    是否只有一個HTML跨度或有更多 ? – HamZa 2013-04-30 10:41:00

    回答

    1

    不需要hacky正則表達式解決方案。一種簡單的循環用狀態機應該做的很好:

    define('STATE_READING', 1); 
    define('STATE_TAG', 2); 
    
    $str = 'Just a <span class="green">little</span> -text åäö width 123#'; 
    $result = ''; 
    
    $state = STATE_READING; 
    for($i = 0, $len = strlen($str); $i < $len; $i++) { 
        $chr = $str[$i]; 
    
        if($chr == '<') { 
         $state = STATE_TAG; 
         $result .= $chr; 
        } else if($chr == '>') { 
         $state = STATE_READING; 
         $result .= $chr; 
        } else if($state == STATE_TAG || strlen(trim($chr)) === 0) { 
         $result .= $chr; 
        } else { 
         $result .= '<span></span>'; 
        } 
    } 
    

    這個循環只是跟蹤,如果我們讀取標籤或單個字符。如果是標籤(或空格),則附加實際字符,否則附加<span></span>

    結果:

    <span></span><span></span><span></span><span></span> <span></span> <span class="green"><span></span><span></span><span></span><span></span><span></span><span></span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span> 
    
    +0

    爲什麼使用define? – 2013-05-02 11:41:41

    +1

    我更喜歡神奇的數字。 '$ state == STATE_TAG'顯示比$ state == 2或$ state =='x''更好的意圖。 – alexn 2013-05-02 11:58:21

    1

    是隻使用一個正則表達式的要求?

    如果不是 - 你可以用一些獨特的字符替換你需要安全的子字符串,用regexp執行替換,把子字符串替換爲唯一的字符。

    就像這樣:

    $str2 = str_replace('<span class="green">little</span>', '$', $str); 
    $str3 = preg_replace("/([^\s\n\$])/", "<span></span>", $str2); 
    $result = str_replace('$', '<span class="green">little</span>', $str3); 
    

    看到現場演示http://codepad.viper-7.com/7wu9fd

    UPD:

    也許應考慮同樣的提示。我的建議是存儲需要存儲的子字符串,替換您需要的所有內容,並將存儲的值存回字符串。

    $str = 'Just a <span class="green">little</span> -text åäö width 123#'; 
    
    preg_match_all('/<[^>]+>/', $str, $matches); 
    $storage=array(); 
    for($i=0, $n=count($matches[0]); $i<$n; $i++) 
    { 
        $key=str_repeat('$', $i+1); 
        $value=$matches[0][$i]; 
        $storage[$key]=$value; 
        $str=str_replace($value, $key, $str); 
    } 
    $storage=array_reverse($storage); 
    
    $str = preg_replace("/([^\s\n\$])/", "<span></span>", $str); 
    foreach($storage as $k=>$v) 
    { 
        $str=str_replace($k, $v, $str); 
    } 
    echo htmlspecialchars($str); 
    

    工作演示是有http://codepad.viper-7.com/L4YZOz

    +0

    有趣的解決方案。太糟糕了,這不是我的選擇。 '小'可以是任何東西,也應該轉換爲跨度。 – 2013-04-30 10:46:05

    +0

    @JensTörnell:查看我的更新回答 – 2013-04-30 11:05:58

    +0

    似乎是正確的。目前已投票。 – 2013-04-30 11:48:12

    0

    雖然這可能是可能的正則表達式,但我會用一個循環去。以下示例代碼適用於單字節字符集,但可以針對多字節(例如UTF-16)或可變字節(例如UTF-8)字符集進行修改。

    $input = 'Just a <span class="green">little</span> -text åäö width 123#'; 
    $output = ''; 
    $length = strlen($input); 
    $i = 0; 
    $matches = array(); // preg_match variable 
    // While for finer control 
    while($i < $length) { 
        // Check for start of span tag, check for < character first for speed-up 
        if($input[$i] == "<" && preg_match("#<span[^>]*>.*</span>#siU", substr($input, $i), $matches) == 1) { 
         // Skip the span tag 
         $i = $i + strlen($matches[0]); 
         $output .= $matches[0]; 
        } else { 
         $output .= "<span></span>"; 
         $i++; 
        } 
    } 
    

    Working example

    +0

    沒有很好的測試代碼,可能是一些邊界條件剩下的,但想法應該清楚。 – dtech 2013-04-30 10:39:46

    0

    黑客攻擊的一位,但試試這個:

    $str="Just a <span class=\"green\">little</span> -text åäö\n width 123#"; 
    
    // get all span tags 
    if(preg_match_all("/(\<span.*\<\/span\>)/", $str, $matches)) 
    { 
        // replace spans with # 
        $str=preg_replace_all("/(\<span.*\<\/span\>)/", "#", $str); 
    
        //print_r($matches); 
    } 
    // replace all non spaces, CR and # 
    $str=preg_replace("/[^\s\n#]/", "<span></span>", $str); 
    // replenish the matched spans 
    while(list($key,$value)=each($matches[0])) 
    { 
        $str=preg_replace('/#/', $value, $str, 1); 
    } 
    
    +0

    如果'$ str'在兩個span標籤之間的某個地方包含一個'#',會不會破壞? – dtech 2013-04-30 10:43:12

    +0

    是的,如果在#集之外有一個#,這就是爲什麼它需要檢查$匹配的黑客 – Waygood 2013-04-30 11:08:44

    0

    所以這裏就是我想出了利用preg_replace_callback()

    $str = 'Just a <span class="green">little</span>-text åäö width 123#<span>aaa</span> lol'; 
    
    // This requires PHP 5.3+ 
    $output = preg_replace_callback('#.*?(<span[^>]*>.*?</span>)|.*#is', function($m){ 
        if(!isset($m[1])){return preg_replace('/\S/', '<span></span>', $m[0]);} 
        $array = explode($m[1], $m[0]); 
        $array = preg_replace('/\S/', '<span></span>', $array); 
        return(implode($m[1], $array)); 
    }, $str); 
    echo($output); 
    

    輸出:

    <span></span><span></span><span></span><span></span> <span></span> <span class="green">little</span><span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span>aaa</span> <span></span><span></span><span></span> 
    
    2

    您可以使用preg_replace_callback()

    $str = 'Just a <span class="green">little</span> -text åäö width 123#'; 
    
    function replacement($matches) { 
          if (strlen($matches[0]) == 1) 
          { 
           return "<span></span>"; 
          } 
          else 
          { 
           return $matches[0]; 
          } 
    } 
    
    $result = preg_replace_callback("~<span.*?<\s*/\s*span>|\S~", "replacement", $str); 
    print_r($result); 
    

    這只是計算替換字符串取決於比賽。如果匹配長度爲1(找到了非空白字符),則用「span」標籤替換,否則找到span標籤,重新插入。

    +0

    +1 [0]不是CR/LF? – Waygood 2013-04-30 11:11:33

    +0

    @Waygood,不,因爲'\ S'是一個非空白字符,換行符屬於空白字符,它們不匹配。 – stema 2013-04-30 11:14:49

    +0

    還有其他屬於'空白字符'的東西嗎?如果它不僅僅是空間和新線,例如tab \ t,那麼結果會錯誤嗎? – Waygood 2013-04-30 11:17:11

    0

    這不是一個正則表達式的哈克方法。這是一個簡潔的一行一功能調用解決方案,它避免了必須迭代字符串中每個字符的一系列條件,保留標記並關心多字節字符。

    alexn的解決方案不保留可見字符長度åäö。他的解決方案將打印6個打開和關閉範圍標籤而不是僅打印3個。這是因爲mb_函數未被使用。關於此主題,請謹慎使用本頁面上未使用mb_前綴字符串函數的任何方法。

    我建議的解決方案將利用(*SKIP)(*FAIL)技術忽略/取消所有遇到的標籤的資格,然後只匹配字符串中的非空白字符。

    代碼:(Demo

    $str = 'Just a <span class="green">little</span> -text åäö width 123#'; 
    var_export(preg_replace('/<[^>]*>(*SKIP)(*FAIL)|\S/','<span></span>',$str)); // no "u" flag means åäö will be span x6 
    echo "\n"; 
    var_export(preg_replace('/<[^>]*>(*SKIP)(*FAIL)|\S/u','<span></span>',$str)); // "u" flag means åäö will be span x3 
    

    輸出:(向右滾動以查看的unicode標誌上的圖案的影響)

    '<span></span><span></span><span></span><span></span> <span></span> <span class="green"><span></span><span></span><span></span><span></span><span></span><span></span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span>' 
    // notice the number of replacements for åäö ->-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------111111111111122222222222223333333333333444444444444455555555555556666666666666 
    '<span></span><span></span><span></span><span></span> <span></span> <span class="green"><span></span><span></span><span></span><span></span><span></span><span></span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span> <span></span><span></span><span></span><span></span><span></span> <span></span><span></span><span></span><span></span>' 
    // notice the number of replacements for åäö ->-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------111111111111122222222222223333333333333 
    
    +0

    @JensTörnell更換多字節字符時,您希望看到多少個span標籤集? åäö'應該變成3組還是6組?在我看來,你只需要三個,因爲六個沒有額外的好處。 – mickmackusa 2017-11-19 06:55:41

    相關問題