Rebol3 Code Examplex


Read a file line by line

Demonstrates line-by-line reading of a text file using a custom Rebol scheme, avoiding full file load into memory.

Rebol [
    title: "Rosetta code: Read a file line by line"
    file:  %Read_a_file_line_by_line.r3
    url:   https://rosettacode.org/wiki/Read_a_file_line_by_line
]

sys/make-scheme [
    name:    'lines
    purpose: "Scheme for reading a text file line by line without loading it all into memory"

    update-lines: function [
        "Read next chunk and extract complete lines into queue"
        state [object!]
    ][
        until [
            if empty? tmp: read/part state/file 4096 [ exit ] ;; EOF -> stop
            parse append state/buffer tmp [
                some [
                    copy line to LF (
                        if CR = last line [take/last line]    ;; strip CR from CRLF
                        append state/lines to string! line
                    ) skip                                    ;; skip LF
                ]
                pos: to end                                   ;; remainder after last LF
            ]
        ]
        truncate pos                    ;; drop processed data, keep tail remainder
    ]
    actor: [
        open: func [port [port!]][
            port/state: context [
                path: as file! ajoin [
                    select port/spec 'path
                    select port/spec 'target
                ]
                file: open/read path    ;; open underlying binary file
                lines: copy []          ;; queue of decoded lines
                buffer: #{}             ;; binary read-ahead buffer
            ]
            port
        ]
        open?: func[port [port!]][
            all [
                object? port/state
                open? port/state/file
            ]
        ]
        close: func[port [port!]][
            if open? port [
                close port/state/file
            ]
            port/state: none
            port
        ]
        read: func[port][
            unless open? port [return none]
            if empty? port/state/lines [
                update-lines port/state  ;; refill line queue from file
            ]
            take port/state/lines        ;; return next line or none at EOF
        ]
    ]
]

;; Usage example:

lines: open lines:Read_a_file_line_by_line.r3
while [ line: read lines ][ probe line ]
close lines

Output:

"Rebol ["
{    title: "Rosetta code: Read a file line by line"}
"    file:  %Read_a_file_line_by_line.r3"
{    url:   https://rosettacode.org/wiki/Read_a_file_line_by_line}
"]"
""
"sys/make-scheme ["
"    name:    'lines"
{    purpose: "Scheme for reading a text file line by line without loading it all into memory"}
""
"    update-lines: function ["
{        "Read next chunk and extract complete lines into queue"}
"        state [object!]"
"    ]["
"        until ["
{            if empty? tmp: read/part state/file 4096 [ exit ] ;; EOF -> stop}
"            parse append state/buffer tmp ["
"                some ["
"                    copy line to LF ("
{                        if CR = last line [take/last line]    ;; strip CR from CRLF}
{                        append state/lines to string! line}
{                    ) skip                                    ;; skip LF}
"                ]"
{                pos: to end                                   ;; remainder after last LF}
"            ]"
"        ]"
{        truncate pos                    ;; drop processed data, keep tail remainder}
"    ]"
"    actor: ["
"        open: func [port [port!]]["
"            port/state: context ["
"                path: as file! ajoin ["
"                    select port/spec 'path"
"                    select port/spec 'target"
"                ]"
{                file: open/read path    ;; open underlying binary file}
{                lines: copy []          ;; queue of decoded lines}
{                buffer: #{}             ;; binary read-ahead buffer}
"            ]"
"            port"
"        ]"
"        open?: func[port [port!]]["
"            all ["
"                object? port/state"
"                open? port/state/file"
"            ]"
"        ]"
"        close: func[port [port!]]["
"            if open? port ["
"                close port/state/file"
"            ]"
"            port/state: none"
"            port"
"        ]"
"        read: func[port]["
"            unless open? port [return none]"
"            if empty? port/state/lines ["
{                update-lines port/state  ;; refill line queue from file}
"            ]"
{            take port/state/lines        ;; return next line or none at EOF}
"        ]"
"    ]"
"]"
""
";; Usage example:"
""
"lines: open lines:Read_a_file_line_by_line.r3"
"while [ line: read lines ][ probe line ]"
"close lines"