kuria/parser

逐字符字符串解析库

v4.0.0 2018-08-05 22:17 UTC

This package is auto-updated.

Last update: 2024-09-22 17:44:41 UTC


README

逐字符字符串解析库。

https://travis-ci.cn/kuria/parser.svg?branch=master

内容

功能

  • 行号跟踪(可禁用以提高性能)
  • 支持 CR、LF 和 CRLF 行结束符
  • 详细异常
  • 提供许多方法来导航和操作解析器
    • 向前/向后预览和查找
    • 向前/向后字符消耗
    • 状态堆栈
  • 字符类型
  • 期望

要求

  • PHP 7.1+

用法

创建解析器

使用字符串输入创建新的解析器实例。

解析器从第一个字符开始。

<?php

use Kuria\Parser\Parser;

$input = 'foo bar baz';

$parser = new Parser($input);

解析器属性

解析器有几个公共属性,可用于检查其当前状态

  • $parser->i - 当前位置
  • $parser->char - 当前字符(或 NULL 在输入末尾)
  • $parser->lastChar - 上一个字符(或 NULL 在输入开始)
  • $parser->line - 当前行(如果禁用行跟踪则为 NULL
  • $parser->end - 输入结束指示符(在末尾为 TRUE,否则为 FALSE
  • $parser->vars - 与当前状态关联的用户定义变量

警告

所有公共属性(除 $parser->vars 之外)都是只读的,并且不得直接由调用代码修改。

使用内置的解析器方法来更改解析器状态。请参阅解析器方法概述

解析器方法概述

有关更多信息,请参阅相应方法的文档注释。

另请参阅字符类型

静态方法

  • getCharType($char): int - 确定字符类型
  • getCharTypeName($charType): string - 获取可读字符类型名称

实例方法

  • getInput(): string - 获取输入字符串
  • setInput($input): void - 替换输入字符串(这也会重置解析器)
  • getLength(): int - 获取输入字符串的长度
  • isTrackingLineNumbers(): bool - 检查是否启用行号跟踪
  • type(): int - 获取当前字符的类型
  • is(...$types): bool - 检查当前字符是否为指定的类型之一
  • atNewline(): bool - 检查解析器是否在换行序列的开始
  • eat(): ?string - 跳到下一个字符并返回它(在末尾返回 NULL
  • spit(): ?string - 跳到上一个字符并返回它(在开始返回 NULL
  • shift(): ?string - 跳到下一个字符并返回它(在末尾返回 NULL
  • unshift(): ?string - 跳到上一个字符并返回它(在开始返回 NULL
  • peek($offset, $absolute = false): ?string - 获取给定偏移量或绝对位置的字符(不修改状态)
  • seek($offset, $absolute = false): void - 修改当前位置
  • reset(): void - 重置状态、变量并回滚到开始位置
  • rewind(): void - 回到开头
  • eatChar($char): ?string - 消费特定字符并返回下一个字符
  • tryEatChar(): bool - 尝试消费特定字符并返回成功状态
  • eatType($type): string - 消费指定类型的所有字符
  • eatTypes($typeMap): string - 消费指定类型的所有字符
  • eatWs(): string - 消费空白字符(如果有)
  • eatUntil($delimiterMap, $skipDelimiter = true, $allowEnd = false): string - 消费直到指定分隔符的所有字符
  • eatUntilEol($skip = true): string - 消费直到行尾或输入结束的所有字符
  • eatEol(): string - 消费行尾序列
  • eatRest(): string - 消费剩余字符
  • getChunk($start, $end): string - 获取输入的片段(不影响状态)
  • detectEol(): ?string - 找到并返回下一个行尾序列(不影响状态)
  • countStates(): int - 获取存储的状态数量
  • pushState(): void - 存储当前状态
  • revertState(): void - 回退到最后存储的状态并弹出它
  • popState(): void - 弹出最后存储的状态而不回退到它
  • clearStates(): void - 丢弃所有存储的状态
  • expectEnd(): void - 确保解析器位于末尾
  • expectNotEnd(): void - 确保解析器不在末尾
  • expectChar($expectedChar): void - 确保当前字符与预期匹配
  • expectCharType($expectedType): void - 确保当前字符是给定的类型

示例 INI 解析器实现

<?php

use Kuria\Parser\Parser;

/**
 * INI parser (example)
 */
class IniParser
{
    /**
     * Parse an INI string
     */
    public function parse(string $string): array
    {
        // create parser
        $parser = new Parser($string);

        // prepare variables
        $data = [];
        $currentSection = null;

        // parse
        while (!$parser->end) {
            // skip whitespace
            $parser->eatWs();
            if ($parser->end) {
                break;
            }

            // parse the current thing
            if ($parser->char === '[') {
                // a section
                $currentSection = $this->parseSection($parser);
            } elseif ($parser->char === ';') {
                // a comment
                $this->skipComment($parser);
            } else {
                // a key=value pair
                [$key, $value] = $this->parseKeyValue($parser);

                // add to output
                if ($currentSection === null) {
                    $data[$key] = $value;
                } else {
                    $data[$currentSection][$key] = $value;
                }
            }
        }

        return $data;
    }

    /**
     * Parse a section and return its name
     */
    private function parseSection(Parser $parser): string
    {
        // we should be at the [ character now, eat it
        $parser->eatChar('[');

        // eat everything until ]
        $sectionName = $parser->eatUntil(']');

        return $sectionName;
    }

    /**
     * Skip a commented-out line
     */
    private function skipComment(Parser $parser): void
    {
        // we should be at the ; character now, eat it
        $parser->eatChar(';');

        // eat everything until the end of line
        $parser->eatUntilEol();
    }

    /**
     * Parse a key=value pair
     */
    private function parseKeyValue(Parser $parser): array
    {
        // we should be at the first character of the key
        // eat characters until = is found
        $key = $parser->eatUntil('=');

        // eat everything until the end of line
        // that is our value
        $value = trim($parser->eatUntilEol());

        return [$key, $value];
    }
}

使用解析器

<?php

$iniParser = new IniParser();

$iniString = <<<INI
; An example comment
name=Foo
type=Bar

[options]
size=150x100
onload=
INI;

$data = $iniParser->parse($iniString);

print_r($data);

输出

Array
(
    [name] => Foo
    [type] => Bar
    [options] => Array
        (
            [size] => 150x100
            [onload] =>
        )

)

字符类型

下表列出了默认字符类型。

这些类型作为常量在Parser类中可用

  • Parser::C_NONE - 无字符(NULL)
  • Parser::C_WS - 空白字符(制表符、换行符、垂直制表符、换页符、回车符和空格)
  • Parser::C_NUM - 数字字符(《0-9》)
  • Parser::C_STR - 字符串字符(《a-z》,《A-Z》、《_》和任何8位字符)
  • Parser::C_CTRL - 控制字符(ASCII 127和ASCII < 32除空白字符外)
  • Parser::C_SPECIAL - !"#$%&'()*+,-./:;<=>?@[\\]^\`{|}~

自定义字符类型

可以通过扩展基本Parser类来自定义字符类型。

以下示例将"-"和"."从CHAR_SPECIAL改为CHAR_STR并继承所有其他内容。

<?php

class CustomParser extends Parser
{
    const CHAR_TYPE_MAP = [
        '-' => self::C_STR,
        '.' => self::C_STR,
    ] + parent::CHAR_TYPE_MAP; // inherit everything else
}

// usage example
$parser = new CustomParser('foo-bar.baz');

var_dump($parser->eatType(CustomParser::C_STR));

输出

string(11) "foo-bar.baz"