1

我意识到以前在这个论坛上也有人问过这个问题,但建议的解决方案对我来说并不可靠。

到目前为止,我已经为此工作了一周或更长时间,昨天我一直熬夜到凌晨 3 点,但我离题了,让我谈谈手头的问题:

对于那些不知道的人,mirc 使用 ascii 控制代码来控制字符颜色、下划线、粗细和斜体。颜色的ASCII码是3,粗体2,下划线1F,斜体1D,反向(黑底白字),16。

作为该数据将要输入的形式的示例,我们有(在正则表达式中,因为这些字符不会打印):

\x034this text is red\x033this text is green\x03 \x02bold text\x02
\x034,3this text is red with a green background\x03

等等。

以下是我尝试修改以供自己使用的两个函数,但返回的结果不可靠。在我进入该代码之前,具体来说“不可靠”,有时代码会解析,有时文本中仍然会留下控制代码,我不知道为什么。反正;

function mirc2html($x) {
    $c = array("FFF","000","00007F","009000","FF0000","7F0000","9F009F","FF7F00","FFFF00","00F800","00908F","00FFFF","0000FF","FF00FF","7F7F7F","CFD0CF");
    $x = preg_replace("/\x02(.*?)((?=\x02)\x02|$)/", "<b>$1</b>", $x);
    $x = preg_replace("/\x1F(.*?)((?=\x1F)\x1F|$)/", "<u>$1</u>", $x);
    $x = preg_replace("/\x1D(.*?)((?=\x1D)\x1D|$)/", "<i>$1</i>", $x);
    $x = preg_replace("/\x03(\d\d?),(\d\d?)(.*?)(?(?=\x03)|$)/e", "'</span><span style=\"color: #'.\$c[$1].'; background-color: #'.\$c[$2].';\">$3</span>'", $x);
    $x = preg_replace("/\x03(\d\d?)(.*?)(?(?=\x03)|$)/e", "'</span><span style=\"color: #'.\$c[$1].';\">$2</span>'", $x);
    //$x = preg_replace("/(\x0F|\x03)(.*?)/", "<span style=\"color: #000; background-color: #FFF;\">$2</span>", $x);
    //$x = preg_replace("/\x16(.*?)/", "<span style=\"color: #FFF; background-color: #000;\">$1</span>", $x);
    //$x = preg_replace("/\<\/span\>/","",$x,1);
    //$x = preg_replace("/(\<\/span\>){2}/","</span>",$x);
    return $x;
}

function color_rep($matches) {
    $matches[2] = ltrim($matches[2], "0");
    $bindings = array(0=>'white',1=>'black',2=>'blue',3=>'green',4=>'red',5=>'brown',6=>'purple',7=>'orange',8=>'yellow',9=>'lightgreen',10=>'#00908F',
        11=>'lightblue',12=>'blue',13=>'pink',14=>'grey',15=>'lightgrey');
    $preg = preg_match_all('/(\d\d?),(\d\d?)/',$matches[2], $col_arr);
    //print_r($col_arr);
    $fg = isset($bindings[$matches[2]]) ? $bindings[$matches[2]] : 'transparent';
    if ($preg == 1) {
        $fg = $bindings[$col_arr[1][0]];
        $bg = $bindings[$col_arr[2][0]];
    }
    else {
        $bg = 'transparent';
    }


    return '<span style="color: '.$fg.'; background: '.$bg.';">'.$matches[3].'</span>';
}

并且,如果它是相关的,在哪里调用代码:

$logln = preg_replace_callback("/(\x03)(\d\d?,\d\d?|\d\d?)(\s?.*?)(?(?=\x03)|$)/","color_rep",$logln);

来源:第一第二

我当然也尝试过查看各种基于 php/ajax 的 irc 客户端完成的方法,但那里没有任何成功。至于做这个 mirc 方面,我也看过那里,虽然结果比 php 更可靠,但发送到服务器的数据呈指数增长,以至于套接字在上传时超时,所以它不是一个可行的选择。

与往常一样,我们将不胜感激在此问题上的任何帮助。

4

1 回答 1

3

您应该划分问题,例如使用分词器。标记器将扫描输入字符串并将特殊部分转换为命名标记,以便脚本的其余部分可以识别它们。使用示例:

$mirc = "\x034this text is red\x033this text is green\x03 \x02bold text\x02
\x034,3this text is red with a green background\x03";

$tokenizer = new Tokenizer($mirc);

while(list($token, $data) = $tokenizer->getNext())
{
    switch($token)
    {
        case 'color-fgbg':
            printf('<%s:%d,%d>', $token, $data[1], $data[2]);
            break;

        case 'color-fg':
            printf('<%s:%d>', $token, $data[1]);
            break;

        case 'color-reset':
        case 'style-bold';
            printf('<%s>', $token);
            break;

        case 'catch-all':
            echo $data[0];
            break;

        default:
            throw new Exception(sprintf('Unknown token <%s>.', $token));
    }
}

这还不算多,但可以确定有趣的部分及其(子)值,如输出所示:

<color-fg:4>this text is red<color-fg:3>this text is green<color-reset> <style-bold>bold text<style-bold>
<color-fgbg:4,3>this text is red with a green background<color-reset>

您应该相对容易地修改上面的循环并处理诸如打开/关闭颜色之类的状态和诸如粗体之类的字体变体标签。

标记器本身定义了一组标记,其中尝试在某个偏移量处(从字符串的开头开始)一个接一个地找到它们。标记由正则表达式定义:

/**
 * regular expression based tokenizer,
 * first token wins.
 */
class Tokenizer
{
    private $subject;
    private $offset = 0;
    private $tokens = array(
        'color-fgbg'  => '\x03(\d{1,2}),(\d{1,2})',
        'color-fg'    => '\x03(\d{1,2})',
        'color-reset' => '\x03',
        'style-bold'  => '\x02',
        'catch-all' => '.|\n',
    );
    public function __construct($subject)
    {
        $this->subject = (string) $subject;
    }
    ...

正如这个私有数组所示,简单的正则表达式和它们的键名。这是上面声明中使用的名称switch

next()函数将在当前偏移量处查找令牌,如果找到,将推进偏移量并返回令牌包括。所有子组匹配。由于涉及偏移量,因此简化了更详细的$matches数组(删除了偏移量),因为主程序通常不需要知道偏移量。

这里的原则很简单:第一个模式获胜。因此,您需要将最匹配的模式(在字符串长度的意义上)放在顶部以使其正常工作。在您的情况下,最大的一个是前景色和背景色的标记,<color-fgbg>.

如果找不到令牌,NULL则返回,所以这里的next()函数:

...
/**
 * @return array|null
 */
public function getNext()
{
    if ($this->offset >= strlen($this->subject))
        return NULL;

    foreach($this->tokens as $name => $token)
    {
        if (FALSE === $r = preg_match("~$token~", $this->subject, $matches, PREG_OFFSET_CAPTURE, $this->offset))
            throw new RuntimeException('Pattern for token %s failed (regex error).', $name);
        if ($r === 0)
            continue;
        if (!isset($matches[0])) {
            var_dump(substr($this->subject, $this->offset));
            $c = 1;
        }
        if ($matches[0][1] !== $this->offset)
            continue;
        $data = array();
        foreach($matches as $match)
        {
            list($data[]) = $match;
        }

        $this->offset += strlen($data[0]);
        return array($name, $data);
    }
    return NULL;
}
...

因此,字符串的标记化现在被封装到Tokenizer类中,并且标记的解析是您可以在应用程序的其他部分中自己做的事情。这应该使您更容易更改样式方式(HTML 输出、基于 CSS 的 HTML 输出或其他不同的东西,如 bbcode 或 markdown),而且将来还支持新代码。此外,如果缺少某些东西,您可以更轻松地修复问题,因为它要么是无法识别的代码,要么是转换中缺少的东西。

作为要点的完整示例:Mirc 颜色和样式(粗体)代码的标记器示例。

相关资源:

于 2012-04-26T09:13:21.827 回答