Php 在字符串中查找不在代码内的点

Php 在字符串中查找不在代码内的点,php,regex,string,bbcode,Php,Regex,String,Bbcode,我有一个字符串,其中包含一篇文章的文本。这在方括号之间撒上BBCODE。我需要能够抓取第一个说,200个字符的文章,而不切断它的中间BBCODE。因此,我需要一个索引,在那里可以安全地将其截断。这将给我文章的总结 摘要必须至少包含200个字符,但要从bbcode中“转义”出来,长度可以更长。这个长度值实际上是函数的一个参数。 在一个独立的bbcode中,它不能给我一个点。请看这样的管道:[lis | t]。 它不能给我一个起点和终点之间的点,比如:[url]=http://www.google.

我有一个字符串,其中包含一篇文章的文本。这在方括号之间撒上BBCODE。我需要能够抓取第一个说,200个字符的文章,而不切断它的中间BBCODE。因此,我需要一个索引,在那里可以安全地将其截断。这将给我文章的总结

摘要必须至少包含200个字符,但要从bbcode中“转义”出来,长度可以更长。这个长度值实际上是函数的一个参数。 在一个独立的bbcode中,它不能给我一个点。请看这样的管道:[lis | t]。 它不能给我一个起点和终点之间的点,比如:[url]=http://www.google.com]转到谷歌网址。 在上面的示例中,它不能在开始或结束bbcode内部或两者之间给我一个点。 它应该给我的安全指数是200后,并没有切断任何代码

希望这是有意义的。我已经为此挣扎了一段时间。我的正则表达式技能一般。谢谢你的帮助

好吧,显而易见的简单答案是,在下面所有正则表达式中不使用任何bbcode驱动的标记来呈现您的摘要

然而,完成您明确描述的工作需要的不仅仅是正则表达式。词法分析器/解析器可以做到这一点,但这是一个相当复杂的主题。我看看能不能想出什么办法

编辑 这是一个相当贫民区版本的lexer,但在这个例子中它是有效的。这会将输入字符串转换为bbcode标记

<?php

class SimpleBBCodeLexer
{
  protected
      $tokens = array()
    , $patterns = array(
        self::TOKEN_OPEN_TAG  => "/\\[[a-z].*?\\]/"
      , self::TOKEN_CLOSE_TAG => "/\\[\\/[a-z].*?\\]/"
    );

  const TOKEN_TEXT      = 'TEXT';
  const TOKEN_OPEN_TAG  = 'OPEN_TAG';
  const TOKEN_CLOSE_TAG = 'CLOSE_TAG';

  public function __construct( $input )
  {
    for ( $i = 0, $l = strlen( $input ); $i < $l; $i++ )
    {
      $this->processChar( $input{$i} );
    }
    $this->processChar();
  }

  protected function processChar( $char=null )
  {
    static $tokenFragment = '';
    $tokenFragment = $this->processTokenFragment( $tokenFragment );
    if ( is_null( $char ) )
    {
      $this->addToken( $tokenFragment );
    } else {
      $tokenFragment .= $char;
    }
  }

  protected function processTokenFragment( $tokenFragment )
  {
    foreach ( $this->patterns as $type => $pattern )
    {
      if ( preg_match( $pattern, $tokenFragment, $matches ) )
      {
        if ( $matches[0] != $tokenFragment )
        {
          $this->addToken( substr( $tokenFragment, 0, -( strlen( $matches[0] ) ) ) );
        }
        $this->addToken( $matches[0], $type );
        return '';
      }
    }
    return $tokenFragment;
  }

  protected function addToken( $token, $type=self::TOKEN_TEXT )
  {
    $this->tokens[] = array( $type => $token );
  }

  public function getTokens()
  {
    return $this->tokens;
  }
}

$l = new SimpleBBCodeLexer( 'some [b]sample[/b] bbcode that [i] should [url="http://www.google.com"]support[/url] what [/i] you need.' );

echo '<pre>';
print_r( $l->getTokens() );
echo '</pre>';

下一步是创建一个解析器,在这些令牌上循环,并在遇到每种类型时采取行动。也许我以后会有时间来做…

这听起来不像是一个只适合正则表达式的工作。 普通编程逻辑是更好的选择:

抓取“[”以外的字符,增加计数器; 如果你遇到一个开始标记,继续前进直到你到达结束标记,不要增加计数器!; 当计数器达到200时,停止抓取文本。
首先,我建议您考虑如何处理一篇完全用BBcodes包装的文章,这在字体标签的情况下通常是正确的。换句话说,解决上述问题的方法很容易导致包含整篇文章的“摘要”。确定哪些标签仍然打开并附加nec可能更有价值请使用必要的BBcodes来关闭它们。当然,在链接的情况下,需要额外的工作来确保您不会破坏它。

这里是一个开始。我目前没有访问PHP的权限,因此您可能需要进行一些调整才能使其运行。此外,这也不会确保标记关闭,即字符串可以有[url]而没有[/url]。此外,如果字符串无效,即并非所有方括号都匹配,则可能无法返回所需内容

def limit(input, length):
  """Splits a text after (length) characters, preserving bbcode"""

  stack = []
  counter = 0
  output = ""
  tag = ""
  insideTag = 0           # 0 = Outside tag, 1 = Opening tag, 2 = Closing tag, 3 = Opening tag, parameters section

  for i in input:
    if counter >= length: # If we have reached the max length (add " and i == ' '") to not make it split in a word
      break
    elif i == '[':        # If we have reached a tag
      insideTag = 1
    elif i == '/':        # If we reach a slash...
      if insideTag == 1:  # And we are in an opening tag
        insideTag = 2
    elif i == '=':        # If we have reached the parameters
      if insideTag >= 1:  # If we actually are in a tag
        insideTag = 3
    elif i == ']':        # If we have reached the closing of a tag
      if insideTag == 2:  # If we are in a closing tag
        stack.pop()       # Pop the last tag, we closed it
      elif insideTag >= 1:# If we are in a tag, parameters or not
        stack.append(tag) # Add current tag to the tag-stack
      if insideTag >= 0:  # If are in some type of tag
        insideTag = 0
        tag = ""
    elif insideTag == 0:  # If we are not in a tag
      counter += 1
    elif insideTag <= 2:  # If we are in a tag and not among the parameters
      tag += i
    output += i

  while len(stack) > 0:
    output += '[/'+stack.pop()+']'   # Add the remaining tags

  return output

cutText = limit('[font]This should be easy:[img]yippee.png[/img][i][u][url="http://www.stackoverflow.com"]Check out this site[/url][/u]Should be cut here somewhere [/i][/font]', 60)
print cutText

我写了这个函数,它应该做你想做的。它计算n个字符数,除了标记中的字符,然后关闭需要关闭的标记。代码中包含示例使用。代码是python的,但应该很容易移植到其他语言,如php


哦,这是一个非常非常好的观点,如果整个事情都被包装在bbcode中会怎样。我可能需要重新思考这个问题。我可能需要修改这个问题,感谢krdluzni关于如果bbcode被包装在整篇文章中会怎样的评论。我想我需要做的是确保截止点不在任何开始或结束代码本身内,并且然后关闭所有未关闭的标签。虽然我不知道如何确定它是否在bbcode标签内。。。
function getIndex($str, $minLen = 200)
{
  //on short input, return the whole string
  if(strlen($str) <= $minLen)
    return strlen($str);

  //get first minLen characters
  $substr = substr($str, 0, $minLen);

  //does it have a '[' that is not closed?
  if(preg_match('/\[[^\]]*$/', $substr))
  {
    //find the next ']', if there is one
    $pos = strpos($str, ']', $minLen);

    //now, make the substr go all the way to that ']'
    if($pos !== false)
      $substr = substr($str, 0, $pos+1);
  }

  //now, it may be better to return $subStr, but you specifically
  //asked for the index, which is the length of this substring.
  return strlen($substr);
}
def limit(input, length):
  """Splits a text after (length) characters, preserving bbcode"""

  stack = []
  counter = 0
  output = ""
  tag = ""
  insideTag = 0           # 0 = Outside tag, 1 = Opening tag, 2 = Closing tag, 3 = Opening tag, parameters section

  for i in input:
    if counter >= length: # If we have reached the max length (add " and i == ' '") to not make it split in a word
      break
    elif i == '[':        # If we have reached a tag
      insideTag = 1
    elif i == '/':        # If we reach a slash...
      if insideTag == 1:  # And we are in an opening tag
        insideTag = 2
    elif i == '=':        # If we have reached the parameters
      if insideTag >= 1:  # If we actually are in a tag
        insideTag = 3
    elif i == ']':        # If we have reached the closing of a tag
      if insideTag == 2:  # If we are in a closing tag
        stack.pop()       # Pop the last tag, we closed it
      elif insideTag >= 1:# If we are in a tag, parameters or not
        stack.append(tag) # Add current tag to the tag-stack
      if insideTag >= 0:  # If are in some type of tag
        insideTag = 0
        tag = ""
    elif insideTag == 0:  # If we are not in a tag
      counter += 1
    elif insideTag <= 2:  # If we are in a tag and not among the parameters
      tag += i
    output += i

  while len(stack) > 0:
    output += '[/'+stack.pop()+']'   # Add the remaining tags

  return output

cutText = limit('[font]This should be easy:[img]yippee.png[/img][i][u][url="http://www.stackoverflow.com"]Check out this site[/url][/u]Should be cut here somewhere [/i][/font]', 60)
print cutText