file stop read when line reached - rebol

I have the code below to begin read the rest of the file after I have found the line containing the string "start"
foreach line clines [
if find [%start] line [
print line
]
]
I can't figure it out from the documentation.. what's going on here? seems logical to me.

You have always to read the file before you can search in the content of the file.
Maybe you are looking for something like
clines: read/lines %myfile
found: false
foreach line clines [
if any [
found
found: find line "start"
] [
print line
]
]
an other way would be
cfile: read %myfile
print all [
cfile: find cfile "start"
find/tail cfile lf
]

Related

In Rebol, what is the idiomatic way to read a text file line by line?

For the purpose of reading a text file line by line, without loading the entire file into memory, what is the common way to do this in Rebol?
I am doing the following, but I think (correct me if I'm wrong) that it loads the whole file into memory first:
foreach line read/lines %file.txt [ print line ]
At least with Rebol2
read/lines/direct/part %file.txt 1
should come near to what you want
but if you want all lines one line after the other, it should be like
f: open/lines/direct %test.txt
while [l: copy/part f 1] [print l]
In theory you can supersede any function, even natives. I will try to give a new foreach
foreach_: :foreach
foreach: func [
"Evaluates a block for each value(s) in a series or a file for each line."
'word [get-word! word! block!] {Word or block of words to set each time (will be local)}
data [series! file! port!] "The series to traverse"
body [block!] "Block to evaluate each time"
/local port line
] [
either any [port? data file? data] [
attempt [
port: open/direct/lines data
while [line: copy/part port 1] [
set :word line
do :body
line
]
]
attempt [close port]
] [
foreach_ :word :data :body
]
]
Probably the set :word line part and the attempt should be more elaborated in order to avoid name clashes and get meaningful errors.
Yes open is the way to go. However like sqlab touches on the necessary /lines & /direct refinements are not present in Rebol 3 open (yet).
The good news though is that you can still use open to read in large files in Rebol 3 without these refinements...
file: open %movie.mpg
while [not empty? data: read/part file 32000] [
;
; read in 32000 bytes from file at a time
; process data
]
close file
So you just need to wrap this up into a buffer and process a line at a time.
Here's a crude working example I've put together:
file: open/read %file.txt
eol: newline
buffer-size: 1000
buffer: ""
lines: []
while [
;; start buffering
if empty? lines [
;; fill buffer until we have eol or EOF
until [
append buffer to-string data: read/part file buffer-size
any [
empty? data
find buffer eol
]
]
lines: split buffer eol
buffer: take/last lines
]
line: take lines
not all [empty? data empty? buffer]
][
;; line processing goes here!
print line
]
close file

How do I write contents of a variable to a text file in Rebol 2?

Newbie question here...
I'd like to write the output of the "what" function to a text file.
So here is what I have done:
I've created a variable called "text" and assigned the output of "what" to it
text: [what]
Now I want to write the content of the "text" variable to a txt file...
Any help is appreciated. Thanks in advance!
the easiest way to write the output of statements to a file is to use
echo %file.log
what
with echo none you end this
>> help echo
USAGE:
ECHO target
DESCRIPTION:
Copies console output to a file.
ECHO is a function value.
ARGUMENTS:
target -- (Type: file none logic)
(SPECIAL ATTRIBUTES)
catch
Unfortunately there is not really a value returned from the what function:
Try the following in the console:
print ["Value of `what` is: " what]
So write %filename.txt [what] will not work.
Instead, what you could do is to look at the source of what
source what
which returns:
what: func [
"Prints a list of globally-defined functions."
/local vals args here total
][
total: copy []
vals: second system/words
foreach word first system/words [
if any-function? first vals [
args: first first vals
if here: find args /local [args: copy/part args here]
append total reduce [word mold args]
]
vals: next vals
]
foreach [word args] sort/skip total 2 [print [word args]]
exit
]
See that this function only prints (it doesn't return the values it finds) We can modify the script to do what you want:
new-what: func [
"Returns a list of globally-defined functions."
/local vals args here total collected
][
collected: copy []
total: copy []
vals: second system/words
foreach word first system/words [
if any-function? first vals [
args: first first vals
if here: find args /local [args: copy/part args here]
append total reduce [word mold args]
]
vals: next vals
]
foreach [word args] sort/skip total 2 [append collected reduce [word tab args newline]]
write %filename.txt collected
exit
]
This function is a little hackish (filename is set, but it will return what you want). You can extend the function to accept a filename or do whatever you want. The tab and newline are there to make the file output prettier.
Important things to notice from this:
Print returns unset
Use source to find out what functions do
write %filename value will write out a value to a file all at once. If you open a file, you can write more times.
Fairly elementary: use write if you just want to save some text, read to recover it; use save if you want to store some data and use load to recover it.
>> write %file.txt "Some Text"
>> read %file.txt
== "Some Text"
>> text: [what]
>> save/all %file.r text
>> load %file.r
== [what]
You can get more information on each word at the prompt: help save or view online: load, save, read and write.

Check if a REBOL string contains another string

I tried using the find function to check for the occurrence of the string "ll" in the string "hello", but it returns "ll" instead of true or false:
"This prints 'll'"
print find "hello" "ll"
Does REBOL's standard library have any functions to check if a string contains another string?
Try this
>> print found? find "hello" "ll"
true
>> print found? find "hello" "abc"
false
#ShixinZeng is correct that there is a FOUND? in Rebol. However, it is simply defined as:
found?: function [
"Returns TRUE if value is not NONE."
value
] [
not none? :value
]
As it is equivalent to not none?...you could have written:
>> print not none? find "hello" "ll"
true
>> print not none? find "hello" "abc"
false
Or if you want the other bias:
>> print none? find "hello" "ll"
false
>> print none? find "hello" "abc"
true
Intended to help with readability while using FIND. However, I don't like it because it has confusing behavior by working when not used with FIND, e.g.
>> if found? 1 + 2 [print "Well this is odd..."]
Well this is odd...
Since you can just use the result of the FIND in a conditional expression with IF and UNLESS and EITHER, you don't really need it very often...unless you are assigning the result to a variable you really want to be boolean. In which case, I don't mind using NOT NONE? or NONE? as appropriate.
And in my opinion, losing FOUND? from the core would be fine.
as written in prior answers find gives you back already a result you can use in conditional expressions.
>> either find "hello" "ll" [
[ print "hit"
[ ] [
[ print "no hit"
[ ]
hit
or in a more rebolish way
>> print either find "hello" "ll" [ "hit"] [ "no hit"]
hit
But you can also use to-logic on the result giving you true or false
>> print to-logic find "hello" "ll"
true
>> print find "hello" "lzl"
none
>> print to-logic find "hello" "lzl"
false

Is it possible to detect esc key with ask function?

Would like to detect esc key to escape the forever loop in pseudo code:
forever [
url: ask "Url: "
if (url = esc) [
break
]
]
Is this possible ?
There is no simple answer, you must use own console-port to handle it correctly, here is the part of it taken from one of my old projects:
REBOL [title: "Console port"]
set 'ctx-console make object! [
system/console/busy: none
system/console/break: false
buffer: make string! 512
history: system/console/history
prompt: "## " ;system/console/prompt
spec-char: none
port: none
init: func[][
port: open/binary [scheme: 'console]
set 's-print get in system/words 'print
set 's-prin get in system/words 'prin
set 'prin func[msg /err /inf /user user-data][
ctx-console/clear-line
s-prin reform msg
ctx-console/print-line
]
set 'print func[msg /err /inf /user user-data][
prin rejoin [reform msg newline]
]
s-prin prompt
]
print-line: func[][
s-prin rejoin [ prompt head buffer "^(1B)[" length? buffer "D"]
]
clear-line: func[][
s-prin rejoin [
"^(1B)[" (
(index? buffer) +
(length? prompt) +
(length? buffer))
"D^(1B)[K"
]
]
key-actions: make block! [
#{08} [;BACK
if 0 < length? head buffer [
buffer: remove back buffer
s-prin rejoin [
"^(back)^(1B)[K"
buffer
"^(1B)["
length? buffer "D"
]
]
]
#{7E} [;HOME
s-prin rejoin ["^(1B)[" (index? buffer) - 1 "D"]
buffer: head buffer
]
#{7F} [;DELETE
buffer: remove buffer
s-prin rejoin ["^(1B)[K" buffer "^(1B)[" length? buffer "D"]
]
#{1B} [;ESCAPE
spec-char: copy/part port 1
either spec-char = #{1B} [
print "ESCAPE"
clear-line
set 'print :s-print
set 'prin :s-prin
system/console/break: true
on-escape
][
switch append spec-char copy/part port 1 [
#{5B41} [;ARROW UP
if not tail? history [
clear-line
clear head buffer
s-prin join prompt buffer: copy history/1
history: next history
buffer: tail buffer
]
]
#{5B42} [;ARROW DOWN
clear-line
buffer: head buffer
clear buffer
if all [
not error? try [history: back history]
not none? history/1
] [
buffer: copy history/1
]
s-prin join prompt buffer
buffer: tail buffer
]
#{5B43} [;ARROW RIGHT
if not tail? buffer [
s-prin "^(1B)[C"
buffer: next buffer
]
]
#{5B44} [;ARROW LEFT
if 1 < index? buffer [
s-prin "^(1B)[D"
buffer: back buffer
]
]
]
]
]
]
do-command: func[comm /local e][
set/any 'e attempt compose [do (comm)]
if all [
not unset? 'e
value? 'e
not object? :e
not port? :e
not function? :e
][
print head clear skip rejoin [system/console/result mold :e] 127
if (length? mold :e) > 127 [
print "...^/"
]
]
]
on-enter: func[input-str /local e][
print rejoin [system/console/prompt input-str]
do-command input-str
]
on-escape: func[][halt]
process: func[/local ch c tmp spec-char err][
ch: to-char pick port 1
either (ch = newline) or (ch = #"^M") [;ENTER
tmp: copy head buffer
if empty? tmp [return none]
history: head history
if any [empty? history tmp <> first history ] [
insert history tmp
]
clear-line
buffer: head buffer
clear buffer
print-line
on-enter tmp
][
switch/default to-binary ch key-actions [
either tail? buffer [
s-prin ch ;either local-echo [ch]["*"]
][
s-prin rejoin ["^(1B)[#" ch]
]
buffer: insert buffer ch
]
]
]
]
ctx-console/init
;and now do something with your own console:
wait-list: reduce [ctx-console/port]
forever [
attempt [ready: wait/all wait-list]
if ready [
ctx-console/process
]
]
You will probably like to change the ctx-console/on-escape and ctx-console/on-enter functions.
As a pure console application, I'm pretty sure the answer is no.
esc is used to cancel the execution of the script.
You can disable that use of esc....
system/console/break: false
....And the esc key now does nothing.
If you switch to REBOL/VIEW and are happy to use a pop up request-text box rather than a console ask line, then you may be able to trap esc using insert-event-func.

Is it possible to override rebol path operator?

It is possible to overide rebol system words like print, make etc., so is it possible to do the same with the path operator ? Then what's the syntax ?
Another possible approach is to use REBOL meta-programming capabilities and preprocess your own code to catch path accesses and add your handler code. Here's an example :
apply-my-rule: func [spec [block!] /local value][
print [
"-- path access --" newline
"object:" mold spec/1 newline
"member:" mold spec/2 newline
"value:" mold set/any 'value get in get spec/1 spec/2 newline
"--"
]
:value
]
my-do: func [code [block!] /local rule pos][
parse code rule: [
any [
pos: path! (
pos: either object? get pos/1/1 [
change/part pos reduce ['apply-my-rule to-block pos/1] 1
][
next pos
]
) :pos
| into rule ;-- dive into nested blocks
| skip ;-- skip every other values
]
]
do code
]
;-- example usage --
obj: make object! [
a: 5
]
my-do [
print mold obj/a
]
This will give you :
-- path access --
object: obj
member: a
value: 5
--
5
Another (slower but more flexible) approach could also be to pass your code in string mode to the preprocessor allowing freeing yourself from any REBOL specific syntax rule like in :
my-alternative-do {
print mold obj..a
}
The preprocessor code would then spot all .. places and change the code to properly insert calls to 'apply-my-rule, and would in the end, run the code with :
do load code
There's no real limits on how far you can process and change your whole code at runtime (the so-called "block mode" of the first example being the most efficient way).
You mean replace (say)....
print mold system/options
with (say)....
print mold system..options
....where I've replaced REBOL's forward slash with dot dot syntax?
Short answer: no. Some things are hardwired into the parser.