php
  • regex
  • string
  • replace
  • preg-replace
  • 2013-04-30 30 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

    相关问题