Içeride BBCode'lar olmayan bir dize bir noktayı bulma

4 Cevap php

Ben bir makalenin metnini içeren bir dize var. Bu (köşeli parantez) BBCode'lar serpilir. Ben bir bbcode ortasında onu kesmeden ilk söz, bir makalenin 200 karakterleri kapmak gerekiyor. Yani onu kesmek için güvenli bir dizin gerekir. Bu bana makale özetini verecektir.

  • Özet en az 200 karakter olmalıdır ama bir bbcode dışarı 'kaçış' olarak uzun olabilir. (Bu uzunluk değeri aslında bir işleve bir parametre olacaktır).
  • [| T lis]: Öyle gibi (boru bakınız) bana tek başına bir bbcode içinde bir noktası vermek gerekir.
  • | Gle [/ url] [url = "http://www.google.com"] Goo Git: Bana şöyle bir başlangıç ​​ve bitiş bbcode arasında bir nokta vermek olmamalıdır.
  • Bu yukarıdaki örnekte, başlangıç ​​veya bitiş bbcode içinde veya arasındaki onlardan bana bir noktası vermek gerekir.

Bana sonra 200 ve herhangi bir BBCode keserek değil "güvenli" indeksi vermelidir.

Umarım bu mantıklı. Ben bir süre için bu ile mücadele edilmiştir. Benim regex becerileri sadece ılımlı vardır. Herhangi bir yardım için teşekkür ederiz!

4 Cevap

Öncelikle, ben bir yazı etiket durumunda genellikle doğru olarak tamamen, BBcode'lar sarılmış bir yazı ile ne yapacağını düşünüyor öneririm. Diğer bir deyişle, belirtilen soruna bir çözüm kolayca 'özetleri', tüm madde içeren yol açacaktır. Bu etiketleri hala açık olduğunu belirlemek ve bunları kapatmak için gerekli BBcode eklemek için daha değerli olabilir. Elbette bir bağlantı durumlarda, bunu kırmak yok sağlamak için ek iş gerektirir.

Peki, belirgin easy cevap Tüm (here alınan aşağıdaki regex) herhangi bir bbcode odaklı biçimlendirme olmadan "özeti" sunmaktır

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

Ancak, açıkça tarif işi sadece bir regex daha fazla ihtiyaç oluyor do. A lexer / çözümleyici hile yapacağını, ama bu orta karmaşık bir konu. Ben bir şey w / gelebilir olmadığını görürsünüz.

EDIT

Burada bir lexer bir oldukça getto versiyonu, ama bu örnek için çalışır. Bu BBCode'u belirteçleri içine bir giriş dizesi dönüştürür.

<?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>';

Bir sonraki adım, bu belirteçleri üzerinde döngüler ve her türlü karşılaştığında gibi tedbirleri alır bir ayrıştırıcı oluşturmak olacaktır. Belki daha sonra bunu yapmak için zaman gerekecek ...

İşte bir başlangıç. Ben şu anda PHP erişiminiz yok, o yüzden çalıştırmak için almak için biraz oynanması gerekebilir. Ayrıca, bu will not etiketleri kapalı olduğundan emin olun (string [/ url] olmadan [url] olabilir yani). Bir dize geçersiz ise de, (yani tüm köşeli parantez eşleştirilir değil) ne istediğinizi dönmek olmayabilir.

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);
}

Ben sadece ne istediğiniz yapmalıyım bu işlevi yazdı. Bu (etiketleri olanlar hariç) karakterlerin n numaralarını sayar ve sonra kapalı olması gerekiyor etiketleri kapatır. Örnek kullanım kodu dahil. Kod python olduğunu, ancak bu php gibi diğer dillerde, bağlantı noktası gerçekten kolay olmalıdır.

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