REBOL 3 Docs Guide Concepts Functions Datatypes Errors
  TOC < Back Next >   Updated: 6-Feb-2009 Edit History  

REBOL 3 Concepts: Series: Searching Series

Pending Revision

This document was written for R2 and has yet to be revised for R3.

The find function searches through block or string series for a value or pattern. This function has many refinements that permit a wide range of variations in search parameters.

Contents

Simple Find

The simplest and most common use of find is to search a block or string for a value. In this case, find requires only two arguments: the series to search and the value to find.

An example of using find on a block is:

colors: [red green blue yellow orange]
where: find colors 'blue
probe where
[blue yellow orange]
print first where
blue

The find function can also search for values by datatype. This can be quite useful.

items: [10:30 20-Feb-2000 Cindy "United"]
where: find items date!
print first where
20-Feb-2000
where: find items string!
print first where
United

An example of using find on a string is:

colors: "red green blue yellow orange"
where: find colors "blue"
print where
blue yellow orange

When a search fails, none! is returned.

colors: [red green blue yellow orange]
probe find colors 'indigo
none

Refinement Summary

Find has many refinements that support a wide variety of search parameters:

Refinement Description
/part Limits a search on a series to a given length or ending position.
/only Treats a series value as a single value.
/case Uses case-sensitive string comparison.
/any Allows the use of pattern wildcards that allow matches to be made with any character. An asterisk () in the pattern matches any string, and a question mark (?) in the pattern matches any character.
/with Allows pattern wildcards with different characters other than asterisk () and (?). This allows a pattern to contain asterisks and question marks.
/match Matches a pattern beginning at the current series position, rather than finding the first occurrence of a value or string. Returns the tail position if the match is found.
/tail Return the tail position of a match on a successful search, rather than returning the point at which the match was found.
/last Searches backwards for the match, starting at the tail of the series.
/reverse Searches backwards for the match, starting at the current position.

Partial Searches

The /part refinement allows a search to be confined to a specific portion of a series. For instance, you may want to restrict a search to a given line or section of text.

Similar to insert/part and remove/part, find/part takes either a count or an ending position. The following example uses a count and restricts the search to the first three items:

colors: [red green blue yellow blue orange gold]
probe find/part colors 'blue
[blue yellow blue orange gold]

The next search is restricted to the first 15 characters:

text: "Keep things as simple as you can."
print find/part text "as" 15
as simple as you can.

The next example uses an ending position. The search is restricted to a single line of text:

text: {
    This is line one.
    This is line two.
}

start: find text "this"
end: find start newline
item: find/part start "line" end
print item
line one.

Tail Positions

The find function returns the position in the series where an item was found. The /tail refinement returns the position immediately following the item that was found. Here's an example:

filename: %script.txt

print find filename "."
.txt
print find/tail filename "."
txt
clear change find/tail filename "." "r"
print filename
script.r

In this example, clear is necessary to remove xt, which follows t.

Backward Searches

The last example in the previous section would fail if the filename had more than one period. For instance:

filename: %new.script.txt
print find filename "."
.script.txt

In this example we want the last occurrence of the period in the string, which can be found using the /last refinement. The /last refinement searches backward through a series.

print find/last filename "."
.txt

The /last refinement can be combined with'/tail to produce:

print find/last filename "."
txt

If you want to continue to search backward through the string, you need the /reverse refinement. This refinement performs a search from the current position backward toward the head, rather than forward toward the tail.

where: find/last filename "."
print where
.txt
print find/reverse where "."
.script.txt

Notice that /reverse continues the search just before the position of the last match. This prevents it from finding the same period again.

Repeated Searches

You can easily repeat the find function to search for multiple occurrences of a value or string. Here is an example that would print all the strings found in a block:

blk: load %script.r
while [blk: find blk string!] [
    print first blk
    blk: next blk
]

The next example counts the number of new lines in a script. It uses the /tail refinement to prevent an infinite loop and returns the position immediately following the match.

text: read %script.r
count: 0
while [text: find/tail text newline] [count: count + 1]

To perform a repeated search in reverse, use the /reverse refinement. The following example prints all of the index positions in reverse order for the text of a script.

while [text: find/reverse tail text newline] [
    print index? text
]

Matching

The /match refinement modifies the behavior of find to perform pattern matching on the current position of a series. This refinement allows parsing operations to be performed by matching the next part of a series with expected patterns. See the chapter on parsing for another way to match series.

A simple example of matching is as follows:

blk: [1342 "Franklin Pike Circle"]
probe find/match blk integer!
["Franklin Pike Circle"]
probe find/match blk 1432
["Franklin Pike Circle"]
probe find/match blk "test"
none
str: "Keep things simple."
probe find/match str "keep"
" things simple."
print find/match str "things"
none

Notice in the example that a search is not performed. The beginning of the series either matches or it does not. If it does match, the series is advanced the position immediately following the match point, allowing you to match the next sequence.

Here is a simple parser written with find/match:

grammar: [
    ["keep" "make" "trust"]
    ["things" "life" "ideas"]
    ["simple" "smart" "happy"]
]

parse-it: func [str /local new] [
    foreach words grammar [
        foreach word words [
            if new: find/match str word [break]
        ]
       if none? new [return false]
       str: next new  ;skip space
   ]
   true
]

print parse-it "Keep things simple"
true
print parse-it "Make things smart"
true
print parse-it "Trust life well"
false

Matching can be made case-sensitive with the /case refinement.

The capability of /match can be greatly extended with the addition of the /any refinement as discussed below.

Wildcard Searches

The /any refinement enables wildcard pattern matching. The question mark (?) and asterisk () characters act as wildcards for matching any single character or any number of characters respectively. The /any refinement can be used in conjunction with find with or without the /match refinement.

Examples:

str: "abcdefg"
print find/any str "c*f"
cdefg
print find/any str "??d"
bcdefg
email-list: [
    mack@rebol.dom
    judy@somesite.dom
    jack@rebol.dom
    biff@rebol.dom
    jenn@somesite.dom
]
foreach email email-list [
    if find/any email *@rebol.dom [print email]
]
mack@rebol.dom jack@rebol.dombiff@rebol.dom

The next example uses the /match refinement to attempt to match the pattern to the next part of the series:

file-list: [
    %rebol.exe
    %notes.html
    %setup.html
    %feedback.r
    %nntp.r
    %rebdoc.r
    %rebol.r
    %user.r
]

foreach file file-list [
    if find/match/any file %reb*.r [print file]
]
rebdoc.rrebol.r

If either of the wildcard characters are part of what is to be matched, substitute wildcard characters can be provided using the /with refinement.

Select

A useful variation of the find function is the select function, which returns the value following the one found. The select function is often used to lookup a value in tagged blocks of data. The select function takes the same arguments as find: the series to search and the value find. However, unlike find, which returns a series position, the select function returns the value that follows the match.

colors: [red green blue yellow orange]
print select colors 'green
blue

Given a simple database, the select function can be used to access its values:

email-book: [
    "George" harrison@guru.org
    "Paul" lefty@bass.edu
    "Ringo" richard@starkey.dom
    "Robert" service@yukon.dom
]

The following code locates a specific email address:

print select email-book "Paul"
lefty@bass.edu

Use the select function to find a block of expressions to evaluate. For example, given the following data:

cases: [
    10 [print "ten"]
    20 [print "twenty"]
    30 [print "thirty"]
]

a block can be evaluated based on a selector'':

do select cases 10
ten
do select cases 30
thirty

Search and Replace

To replace values throughout a series, you can use the replace function. This function searches for a specific value in a series, then replaces it with a new value.

The replace function takes three arguments: the series to search, value to replace, and the new value.

str: "hello world hello"
probe replace str "hello" "aloha"
"aloha world hello"
data: [1 2 8 4 5]
probe replace data 8 3
[1 2 3 4 5]
probe replace data 4 `four
[1 2 3 four 5]
probe replace data integer! 0
[0 2 3 four 5]

Use the /all refinement to replace all occurrences of the value from the current position to the tail.

probe replace/all data integer! 0
[0 0 0 four 0]
code: [print "hello" print "world"]
replace/all code 'print 'probe
probe code
[probe "hello" probe "world"]
do code
helloworld
str: "hello world hello"
probe replace/all str "hello" "aloha"
"aloha world aloha"


  TOC < Back Next > REBOL.com - WIP Wiki Feedback Admin