【问题标题】:Rebol 3: reading STDIN efficiently line by line (to make awk like tool)Rebol 3:逐行有效地读取 STDIN(制作类似 awk 的工具)
【发布时间】:2017-01-27 07:49:02
【问题描述】:

我正在尝试制作一个类似 awk 的工具,该工具使用 Rebol 3 来处理带有 bash 管道和工具的更大文本文件。我在 Rebol 3 中逐行读取 STDIN 时遇到问题?

例如,这个 shell 命令产生 3 行:

$ (echo "first line" ; echo "second line" ; echo "third line" )
first line
second line
third line

但是 Rebol 的 input 字会同时读取所有 3 行。如果您以交互方式使用输入,我希望它停止在换行符处。

r3 --do 'while [ x: input ] [ if empty? x [ break ] print x print   "***" ]' 
abcdef
abcdef
***
blabla
blabla
***

但是当我一起运行它时,它会一次读取整个输入。我可以一次阅读所有内容并分成几行,但我希望它以“流式”方式工作,因为我通常 cat 在许多 1000 行中。

$ (echo "first line" ; echo "second line" ; echo "third line" )  \
  | r3 --do 'while [ x: input ] [ if empty? x [ break ] print x print "***" ]' 
first linesecond linethird line
***

我还查看了 input 的来源以制作类似的功能。我可以在 while 循环中读取每个字符的字符并检查 newlines 但这似乎效率不高。

【问题讨论】:

    标签: stdin rebol rebol3


    【解决方案1】:

    我想通了,即使在 10000 行的大文件上,它似乎也能很好地工作。不过,它可以写得更优雅和改进。

    函数 r3awk 采用 STDIN 和它每行执行的代码块,并将行变量绑定到它:

    r3awk: func [ code /local a lines line partial ] [ 
        partial: copy ""
        lines: read/lines/string system/ports/input
        while [ not empty? lines ] [
            lines/1: rejoin [ partial lines/1 ]
            partial: pull lines
            foreach line lines [
                do bind code 'line
            ] 
            if error? try [ lines: read/lines/string system/ports/input ] [ lines: copy [] ]
        ]
        line: partial
        do bind code 'line
    ]    
    

    它是这样工作的。 read/lines 从流中读取多个字符并返回一个行块。每次调用它都会读取下一批这样的字符,所以它都包含在一个 while 循环中。代码处理(执行代码块)作为 while 循环(不是在最后)。

    这批字符不会以换行符结尾,所以最后一行每次都是部分。下一批中的第一行也是如此,因此它将它们连接在一起。最后,它必须处理最后一行(这次是非部分行)。 尝试是因为有些行导致utf编码错误。

    在命令行中可以这样使用:

    (echo "first line" ; echo "second line" ; echo "third line" ) | \
     r3 --import utils.r --do 'r3awk [ parse line [ copy x to space (print x) ] ]'
    first
    second
    third
    

    需要改进的地方:使功能总体上更好,对一些代码进行重复数据删除。检查如果 read/lines 确实在换行符上结束会发生什么。

    【讨论】:

    • 很好地找到了关于read/lines 在 STDIN 上的缓冲。但是它在 MacOS (OSX) 上对我来说并不完全有效 :( 而不是 block! 它返回 34815 字节的 binary! (直到 STDIN 用尽)。注意。事实上 /lines (和 @987654327 @) 不要在 MacOS 上做任何事情 :(
    【解决方案2】:

    几年前我遇到了与input 相同的问题。我不认为这是一个有计划的改变,而是一个不完整的实施(碰木头!)。

    这是我当时编写的一个解决方法(在 MacOS 和 Linux 上对我来说效果很好)。

    input-line: function [
        {Return next line (string!) from STDIN.  Returns NONE when nothing left}
        /part size [integer!] "Internal read/part (buffer) size"
      ][
        buffer: {}    ;; static
        if none? part [size: 1024]
    
        forever [
            if f: find buffer newline [
                remove f    ;; chomp newline (NB. doesn't cover Windows CRLF?)
                break
            ]
    
            if empty? data: read/part system/ports/input size [
                f: length? buffer
                break
            ]
    
            append buffer to-string data
        ]
    
        unless all [empty? data empty? buffer] [take/part buffer f]
    ]
    

    使用示例:

    while [not none? line: input-line] [
        ;; do something with LINE of data from STDIN
    ]
    

    【讨论】:

      猜你喜欢
      • 2012-04-09
      • 2012-08-11
      • 1970-01-01
      • 2011-06-25
      • 2014-01-15
      • 1970-01-01
      • 2012-12-10
      • 2019-06-26
      • 2017-04-28
      相关资源
      最近更新 更多