0

記事のテキストを含む文字列があります。これにはBBCodes(角括弧の間)が散りばめられています。私は最初の言葉、bbcodeの途中でそれを切り落とすことなく記事の200文字をつかむことができる必要があります。だから私はそれを切り落とすのが安全なインデックスが必要です。これで記事の概要がわかります。

  • 要約は200文字以上である必要がありますが、bbcodeから「エスケープ」するためにもっと長くすることができます。(この長さの値は、実際には関数のパラメーターになります)。
  • [lis | t]のように、スタンドアロンのbbcode(パイプを参照)の内部にポイントを与えてはなりません。
  • [url = "http://www.google.com"] Go To Goo | gle[/url]のように開始と終了のbbcodeの間にポイントを与えてはなりません。
  • 上記の例では、開始または終了のbbcodeの内部、あるいはそれらの中間にポイントを与えてはなりません。

それは私に200以降でBBCodesを切断していない「安全な」インデックスを与えるはずです。

これが理にかなっていることを願っています。私はしばらくこれに苦労してきました。私の正規表現スキルは中程度です。助けてくれてありがとう!

4

5 に答える 5

4

まず、フォントタグの場合によくあることですが、完全にBBcodeでラップされた投稿をどうするかを検討することをお勧めします。言い換えれば、述べられた問題の解決策は、記事全体を含む「要約」に簡単につながるでしょう。どのタグがまだ開いているかを識別し、それらを閉じるために必要なBBcodeを追加する方がより価値があるかもしれません。もちろん、リンクの場合は、リンクを壊さないようにするために追加の作業が必要になります。

于 2009-07-28T20:40:10.580 に答える
2

さて、明らかな簡単な答えは、bbcode駆動のマークアップをまったく使用せずに「要約」を提示することです(以下の正規表現はここから取得)

$summary = substr( preg_replace( '|[[\/\!]*?[^\[\]]*?]|si', '', $article ), 0, 200 );

ただし、明示的に説明する仕事は、単なる正規表現以上のものを必要とします。レクサー/パーサーでうまくいきますが、それはやや複雑なトピックです。私は何かを思い付くことができるかどうかを確認します。

編集

これはレクサーのかなりのゲットーバージョンですが、この例では機能します。これにより、入力文字列が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>';

次のステップは、これらのトークンをループし、各タイプに遭遇したときにアクションを実行するパーサーを作成することです。多分私は後でそれを作る時間があるでしょう...

于 2009-07-28T20:38:58.757 に答える
1

これは、(唯一の)正規表現の仕事のようには聞こえません。「プレーンプログラミング」ロジックはより良いオプションです:

  • '['以外の文字を取得し、カウンターを増やします。
  • 開始タグに遭遇した場合は、終了タグに到達するまで前進し続けます(カウンターを増やさないでください)。
  • カウンターが200に達したら、テキストの取得を停止します。
于 2009-07-28T20:39:59.813 に答える
0

私はあなたが望むことをするはずのこの関数を書きました。n個の文字(タグ内の文字を除く)をカウントしてから、閉じる必要のあるタグを閉じます。コードに含まれている使用例。コードはPythonですが、phpなどの他の言語への移植は非常に簡単です。

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
于 2009-08-15T11:17:35.667 に答える
0

これが始まりです。現在、PHPにアクセスできないため、実行するには微調整が必​​要になる場合があります。また、これはタグが閉じていることを保証しません(つまり、文字列には[/ url]なしで[url]が含まれる可能性があります)。また、文字列が無効な場合(つまり、すべての角かっこが一致していない場合)、目的の文字列が返されない可能性があります。

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);
}
于 2009-07-28T20:40:41.763 に答える