stubbles / streams
输入输出流。
Requires
- php: ^8.2
Requires (Dev)
- ext-iconv: *
- bovigo/assert: ^8.0
- bovigo/callmap: ^8.0
- mikey179/vfsstream: ^1.6.11
- phpunit/phpunit: ^10.5
- stubbles/reflect: ^10.0
- stubbles/sequence: ^10.1
Suggests
- ext-iconv: For use in decoding and encoding stream decorators.
- stubbles/sequence: Easer iteration of input streams with lines() function.
README
输入输出流。
构建状态
安装
stubbles/streams 以 Composer 包的形式分发。要将它作为你包的依赖项安装,请使用以下命令
composer require "stubbles/streams": "^10.0"
需求
stubbles/streams 至少需要 PHP 8.2。
要使用编码和解码装饰流,需要 PHP 扩展 iconv。
要使用 stubbles\streams\linesOf()
和 stubbles\streams\nonEmptyLinesOf()
函数,需要包 stubbles/sequence。
接口和方法
stubbles/streams 为不同类型的来源提供输入和输出流。所有输入流实现 stubbles\streams\InputStream
接口,所有输出流实现 stubbles\streams\OutputStream
接口。输入流可用于从来源读取数据,输出流可用于将数据写入特定来源。
接口及其方法
输入流方法
输入流接口提供了以下方法
read($length = 8192)
- 返回指定数量的字节readLine($length = 8192)
- 返回直到下一个换行符的所有字符,直到指定长度bytesLeft()
- 返回剩余要读取的字节数eof()
- 检查是否到达输入的末尾close()
- 关闭流
输出流方法
输出流接口提供了以下方法
write($bytes)
- 写入指定字节并返回写入的字节数writeLine($bytes)
- 与write()
相同,但在字节末尾添加换行符close()
- 关闭流
可寻址流
某些流是可寻址的,这意味着您可以从一个流中的位置跳转到另一个位置。可寻址的输入流实现 stubbles\streams\Seekable
接口。它提供了以下方法
seek($offset, $whence = Seekable::SET)
- 将内部流指针设置为给定位置tell()
- 返回内部流指针的当前位置
从输入流到输出流的复制
自 8.1.0 版本起可用
快捷函数 stubbles\streams\copy()
提供了一种简单的方法,将输入流中的所有内容复制到输出流中
copy($in)->to($out);
请注意,复制从输入流当前所在的偏移量开始,并更改输入流的偏移量为流的末尾。如果复制的输入流是不可寻址的,它不能返回到初始偏移量。
装饰流
编码相关的流
有时需要将输入流数据解码为应用程序的内部编码。虽然 Stubbles 的内部编码是 UTF-8,但所有从输入流读取的数据都应该是 UTF-8 本身或至少在从输入流返回之前转换为 UTF-8。为了简化这一点,stubbles\streams\DecodingInputStream
类在构造时装饰了另一个输入流(作为第一个参数给出),并尝试使用 iconv()
将装饰的输入流中读取的数据从流编码解码为 UTF-8。但是,您需要将装饰的输入流的字符集指定为构造函数的第二个参数
$decodingInputStream = new DecodingInputStream($encodedInputStream, 'iso-8859-1');
注意:自 9.0.0 版本起,可以使用第三个参数来影响数据被编码成什么。默认值仍然是 UTF-8。
当然必须有一种可能写入正确的编码。为此,可以使用 stubbles\streams\EncodingOutputStream
类。它尝试使用 iconv()
将内部 UTF-8 编码转换为装饰输出流的编码。
$encodingOutputStream = new EncodingOutputStream($encodedOutputStream, 'iso-8859-1');
注意:从版本 9.0.0 开始,可以使用第三个参数来影响数据被编码的方式。默认仍然是 UTF-8。
与文件相关的流
要从文件读取数据,可以使用 stubbles\streams\file\FileInputStream
类。其构造函数期望传入一个文件名,或一个已经打开的文件指针资源。如果是文件名,构造函数的第二个参数设置文件打开的模式,默认为 rb(二进制读取)。文件输入流是一个可寻址流。
要将数据写入文件,可以使用 stubbles\streams\file\FileOutputStream
类。与文件输入流类类似,其构造函数也期望传入一个文件名或一个已经打开的文件指针资源。如果是文件名,构造函数的第二个参数设置文件打开的模式,默认为 wb(二进制写入)。
警告:模式参数接受可能与流类不兼容的模式 - 例如,输入流允许将 wb 作为模式参数的值,但此时您无法从提供的文件中读取,反之亦然。
内存流
有时,如果可以将数据读取或写入到内存中,这会很有帮助。为此,存在 stubbles\streams\memory\MemoryInputStream
和 stubbles\streams\memory\MemoryOutputStream
。虽然内存输入流类期望以字符串参数的形式在构造函数中读取内容,但内存输出流在构造时不需要值,但它提供了一个额外的 buffer()
方法,该方法返回到目前为止已写入此流的所有数据。此外,将 stubbles\streams\memory\MemoryOutputStream
转换为字符串将返回缓冲区(自版本 4.0.0 起可用)。
内存输入流是一个可寻址流。
过滤器流
当从流中读取或写入数据时,有时可能会出现不是所有要读取或要写入的数据都与您要实现的目标相关的情况。例如,您读取的文件可能包含注释行,您不希望忽略这些注释行。通常,您会在读取类中实现什么要忽略的逻辑
while (!$inputStream->eof()) { $line = $inputStream->readLine(); if (substr(0, 1, $line) !== '#') { $this->processLine($line); } }
在这个小例子中,可能不会费太多事,但如果注释行也可能以 //
开头,或者甚至可能有跨多行的注释,就像我们在 PHP 中用 /* 多行注释 ... */
一样,此时的逻辑可能会变得有点复杂。这时,流过滤器就派上用场了
$filterStream = new FilteredInputStream( $inputStream, function($line) { return substr($line, 0, 1) !== '#'; } ); while (!$filterStream->eof()) { $this->processLine($inputStream->readLine()); }
stubbles\streams\filter\FilteredInputStream
的第二个参数可以是任何接受字符串作为参数并返回 true
(表示应传递此字符串)或 false
(表示应过滤此字符串)的 callable
。请注意,您不能从传递的字符串中过滤单个字符,只能过滤整个字符。
对 stubbles\streams\filter\FilteredOutputStream
也适用,只是过滤器应用于要写入的数据。
输入流序列
由于使用 while
迭代输入流可能会很麻烦,stubbles/streams 提供了一个也是 \Iterator
实例的输入流实现。
$lines = new InputStreamIterator(new FileInputStream('somefile.txt')); foreach ($lines as $line { processLine($line); }
每次迭代步骤是对装饰输入流的 readLine()
的调用。
请注意:如果装饰流不是 stubbles\streams\Seekable
的实例,则只能迭代一次,尝试重绕将不会产生任何效果。
与 stubbles/sequence 的集成
当包 stubbles/sequence 可用时,迭代甚至可以进一步增强。
这允许使用两个函数,它们返回一个 stubbles\sequence\Sequence
实例,允许在输入流上进行所有序列操作
stubbles\streams\linesOf($input)
输入可以是 stubbles\streams\InputStream
的一个实例或一个文件名。
$lines = linesOf('somefile.txt') ->filter(/* callable which filters */) ->map(/* callable which maps the line to other content */); foreach ($lines as $line) { processLine($line); }
stubbles\streams\nonEmptyLinesOf($input)
与上面相同,但已经过滤掉了所有空行。