Chapter 7: HANDLING STRING DATA: STRING VARIABLES AND "SYMBOLS"
Ferret offers a variety of tools for manipulating strings through the use of "symbols" (variables defined to be strings). In addition, beginning with Version 5.4, string variables are supported, with much the same syntax as for numeric variables.
String variables are defined using DEFINE VARIABLE (or its alias LET). They can be read from and written to netCDF files. Arrays of strings may be defined and a limited number of algebraic operations are defined for string variables. They can be passed to go scripts and functions. Grave accents around a scalar string return the string.
yes? LET astring = "hello everyone"
yes? LIST/NOHEAD astring
"hello
everyone"
yes? message `astring`
!-> message hello everyone
hello everyone
Hit Carriage Return to continue
Strings in arrays may be of variable length. The syntax {"a","b","c"} denotes an array of strings. Two commas in a row denotes a null (missing value) string. Single and double quoted strings are both allowed, but must match for an individual string, e.g. 'P" is not valid.
Examples: the following define and list valid string arrays:
yes? LET a = {"s1","s2", ,"s3"}
yes? LIST a
{"s1","s2", ,"s3"}
1 / 1:"s1"
2 / 2:"s2"
3 / 3:""
4 / 4:"s3"
yes? LET b = {, 'string1','s2',,'cccc'}
yes?
LIST/i=3:5 b
{, 'string1','s2',,'cccc'}
3 / 3:"s2"
4 /
4:""
5 / 5:"cccc"
yes? LET c = {'p', 'q', 'a longer string'}
yes?
LIST/NOHEAD/ORDER=x c
"p" "q" "a longer string"
A number of functions are available for working with string variables. They are described in the following sections. See also the section later in this chapter, "PLOT+ string editing tools" (p. 240) for more ways to manipulate
Ch7 Sec2.1. STRCMP(string1, string2)
string1: string
string2: string
result: real
Compares two strings or string arrays. It computes the numerical difference in ASCII character value for the first character that differs between the two strings returns a number, > 0 if string1 > string2, 0 if they are equal and < 0 if string1 < string2.
Examples:
yes? list strcmp("b",{"a","b","c"})
VARIABLE : STRCMP("b",{"a","b","c"})
SUBSET : 3 points (X)
1 / 1: 1.000
2 / 2: 0.000
3
/ 3: -1.000
yes? list strcmp({"a","b","c"},YSEQUENCE({"a","b","c"}))
VARIABLE : STRCMP({"a","b","c"},YSEQUENCE({"a","b","c"}))
SUBSET : 3 by 3 points (X-Y)
1 2 3
1 2 3
1 / 1: 0.000 1.000 2.000
2 / 2: -1.000
0.000 1.000
3 / 3: -2.000 -1.000 0.000
yes? let a = "a longer string"
yes?
list strcmp (a,"a longer stringg")
VARIABLE : STRCMP (a,"a
longer stringg")
-103.0
string1: string
result: real
Returns the length of the string passed in string string1
string1: string
result: string
Returns the string passed in string string1 in all upper case characters
string1: string
result: string
Returns the string passed in string string1 in all lower case characters
Ch7 Sec2.5. STRINDEX(string1, substring)
string1: string
substring: string
result: real
Locate first occurrence of substring in string1. Returns a 0 if substring doesn't exist in string1. If substring contains a zero-character string (i.e. ""), the function returns 1.
Ch7 Sec2.6. STRRINDEX(string1, substring)
string1: string
substring: string
result: real
Locates last occurence of string substring in string1. Returns a 0 if substring doesn't exist in string1. If substring contains a zero character string (i.e. ""), the function returns the length of string1
Ch7 Sec2.7. SUBSTRING(string1, offset, len)
string1: string
offset: integer
len: integer
result: string
Returns substring of length len from string string1 beginning from character offset in string1. If offset is 0, or if offset is greater than the length of string string1, a NULL value is returned. If length len exceeds the total length of string string1, the value of string string1 starting at offset is returned.
Ch7 Sec2.8. STRCAT(string1, str2)
string1: string
string2: string
result: string
Append a copy of string string2 onto string string1.
string1: string
result: real
Return float value of string string1 (e.g. STRFLOAT("3.14"))
Ch7 Sec2.10. LABWID(string, charsize)
string: string, possibly including a font specification, and may include
<NL> newline character
charsize: real
result: real
Returns the size in plot-page inches of the string. If a font is specified, it takes the size of characters in that font into account. If the string is a multi-line label, the result is computed for the longest line as it will appear on the plot page.
Examples:
! LABWID with two different character sizes,
! lowercase
vs capitals, and the italics font
yes? LIST/NOHEADER LABWID("abcdefg",
.15)
0.8857
yes? LIST/NOHEADER LABWID("abcdefg", .10)
0.5905
yes? LIST/NOHEADER LABWID("ABCDEFG", .15)
0.9929
yes? LIST/NOHEADER LABWID("@IIabcdefg", .15)
1.0000
! multi-line label: result is the length of the longest line
yes? LIST/NOHEADER
LABWID("abcdefg<NL>hi", .15)
0.8857
! Make a plot, drawing
one label after another.
! Use the italics font for the first label.
yes? PLOT/VS/LINE/I=1:314 i*cos(i/20),i*sin(i/20)
yes? LET firstlab
= "@iiMy label"
yes? LABEL/NOUSER 0, 3, -1, 0, .15, "`firstlab`"
yes?
LET wid = labwid("`firstlab`", .15)
yes? LABEL/NOUSER `wid`, 3, -1, 0,
.15, "@P4Second Label"
The SPAWN command executes a Unix system command and returns the result in a string array. The syntax SPAWN:"command" inside a string array definition allows the output of a Unix command to be mixed with other strings.
Examples:
LET a = {"first.nc", SPAWN:"ls *.nc","last.nc"}
Say we want to check whether a file is in the directory. We do not want a null result, so start with a dummy string. If "myfile.nc" exists, there will be 2 entries in array a.
yes? LET a = {"dummy", SPAWN:"ls myfile.nc"}
yes? LET nfiles = `a,RETURN=IEND`
yes? IF `nfiles EQ 2` THEN ...
Ch7 Sec2.12. Algebraic operations with string variables.
A number of algebraic operations are available for string variables. They are described in the following sections.
Ch7 Sec2.12.1. Logical operators with strings
The operators EQ, LT, LE, ... can be applied to string variables and arrays. These operators are case-insensitive (functions will be provided later that are case-sensitive and include UPCASE, DNCASE)
Examples:
yes? LIST/NOHEAD {"a","b","c"} EQ {"A","B","C"} ! case insensitive
1
/ 1: 1.000
2 / 2: 1.000
3 / 3: 1.000
yes? LIST/NOHEAD"b" GT {"a","b","c"}
1 / 1: 1.000
2 / 2: 0.000
3
/ 3: 0.000
Ch7 Sec2.12.2. Shift transformation of string arrays
The shift transformation can be applied to string arrays.
For example:
yes? LET a = {"a","b","c","d"}
yes? LIST a[i=@SHF]
{"a","b","c","d"}
shifted by 1 pts on X
1 / 1:"b"
2 / 2:"c"
3 / 3:"d"
4
/ 4:""
yes? LIST a[i=@SHF:-1]
{"a","b","c","d"}
shifted by -1 pts on X
1 / 1:""
2 / 2:"a"
3 / 3:"b"
4 / 4:"c"
Ch7 Sec2.12.3. Strings in IF-THEN-ELSE
IF cond THEN string_array1 ELSE string_array2
Example:
yes? LIST/NOHEAD IF {0,1} THEN "hello" ELSE "goodbye"
1 / 1:"goodbye"
2 / 2:"hello"
Ch7 Sec2.12.4. String concatenation with "+":
Examples:
yes? let a = "good" + "bye"
yes? LIST/NOHEAD YSEQUENCE({"now","then"})+", " + (if {0,1} THEN "hello"+",
") + "friend"
1 / 1:"now, friend" "now, hello, friend"
2 / 2:"then,
friend" "then, hello, friend"
Ch7 Sec2.12.5. Strings as Function arguments
A few functions also take strings as arguments. String arguments must be enclosed in double quotes. For example, a function to write variable "u" into a file named "my_output.v5d", formatted for the Vis5D program might be implemented as
LOAD WRITE_VIS5D("my_output.v5d", a)
SAMPLE* functions may take string or numerical arrays as arguments
Example:
yes? LIST/NOHEAD SAMPLEI({"a","b","c","d","e","f"},{3,2,,1})
1 / 1:"c"
2 / 2:"b"
3 / 3:""
4 / 4:"a"
yes? LIST/NOHEAD SAMPLEJ(YSEQUENCE ({"a","b","c","d","e","f"}), {3,2,,1})
1 / 1:"c"
2 / 2:"b"
3 / 3:""
4 / 4:"a"
Ch7 Sec2.12.6. Regridding string arrays
The regridding transformations @ASN, @XACT, @NRST can be used with character data.
Examples:
yes? LET a = {spawn:"ls *.nc"}
yes? LIST a
{SPAWN:"ls *.nc"}
1 / 1:"d1.nc"
2 / 2:"d2.nc"
3 / 3:"d3.nc"
4 / 4:"d4.nc"
5 / 5:"d5.nc"
6 / 6:"d6.nc"
7 / 7:"d7.nc"
yes? DEFINE AXIS/X=0.1:0.7:.1 xasn
yes? LIST
a[gx=xasn@ASN]
{SPAWN:"ls *.nc"}
regrid: 0.1 delta
on X@ASN
0.1 / 1:"d1.nc"
0.2 / 2:"d2.nc"
0.3 / 3:"d3.nc"
0.4 /
4:"d4.nc"
0.5 / 5:"d5.nc"
0.6 / 6:"d6.nc"
0.7 / 7:"d7.nc"
yes? DEFINE
AXIS/X=1:6:.5 xxact
yes: LIST a[gx=xxact@XACT]
{SPAWN:"ls *.nc"}
regrid: 0.5 delta on X@XACT
1 / 1:"d1.nc"
1.5 / 2:""
2 / 3:"d2.nc"
2.5 / 4:""
3 / 5:"d3.nc"
3.5 / 6:""
4 / 7:"d4.nc"
4.5 / 8:""
5 / 9:"d5.nc"
5.5 / 10:""
6 / 11:"d6.nc"
yes? DEFINE AXIS/X=1:6:.4 xnrst
yes? LIST a[gx=xnrst@NRST]
{SPAWN:"ls *.nc"}
regrid: 0.4 delta on X@NRST
1
/ 1:"d1.nc"
1.4 / 2:"d1.nc"
1.8 / 3:"d2.nc"
2.2 / 4:"d2.nc"
2.6
/ 5:"d3.nc"
3 / 6:"d3.nc"
3.4 / 7:"d3.nc"
3.8 / 8:"d4.nc"
4.2
/ 9:"d4.nc"
4.6 / 10:"d5.nc"
5 / 11:"d5.nc"
5.4 / 12:"d5.nc"
5.8
/ 13:"d6.nc"
6.2 / 14:"d6.nc"
Ch7 Sec2.13. NetCDF input and output of string data
String variables can be input and output to netCDF files. In the file the string axis is the fastest moving dimension and all strings are the same length (equal to the maximum length of the strings being written). Extra character spaces are padded with nulls. If variable length strings are written out, then when read back they will again be variable length.
Example:
yes? SAVE/CLOBBER/FILE=test_string.cdf/HEADING=enhanced a[i=2:4]
The following are the relevant commands:
usage:
DEFINE SYMBOL symbol_name = string
SHOW SYMBOL
usage:
SHOW SYMBOL/ALL
SHOW SYMBOL symbol_name
SHOW SYMBOL partial_name
CANCEL SYMBOL
usage:
CANCEL SYMBOL/ALL
CANCEL SYMBOL symbol_name
Legal symbol names must begin with a letter and contain only letters, digits, underscores, and dollar signs.
To invoke symbol substitutionthe replacement of the symbol name with its (text) valuewithin a Ferret command include the name of the symbol preceded by a dollar sign in parentheses.
For example,
yes? DEFINE SYMBOL hi = hello everyone
yes? MESSAGE ($hi) !
issues "hello everyone" msg
It is also possible to nest symbol definitions, as the following commands illustrate:
yes? DEFINE SYMBOL label_2 = My test label
yes? DEFINE SYMBOL number = 2
yes?
SAY ($label_($number))
My test label
Ch7 Sec4. AUTOMATICALLY GENERATED SYMBOLS
A number of useful symbols are automatically defined whenever Ferret sets up a plot. Following any plotting command issue the command SHOW SYMBOLS/ALL to see a list. Consult the PLOT PLUS for Ferret Users Guide (section "General Global Symbols") for detailed descriptions of the plot symbols. For example, if we wish to place a label hello at the upper right corner of a plot we might do the following
yes? PLOT/I=1:100 SIN(I/6)
yes? LABEL/NOUSER ($ppl$xlen) ($ppl$ylen) 1 0
.2 hello
This labeling procedure would work regardless of the aspect ratio of the plot. Use the command SHOW SYMBOL/ALL to see the symbols (and see "General Global Symbols" in the PLOT+ Users Guide).
Ch7 Sec5. USE WITH EMBEDDED EXPRESSIONS
When used together with Ferret embedded expressions symbols can be used to perform arithmetic on the plot geometry. For example, this command will locate the plot title in bold at the center of a plot regardless of the aspect ratio:
yes? LABEL/NOUSER `($ppl$xlen)/2` `($ppl$ylen)/2` 0 0 .2 @AC($labtit)
Ch7 Sec6. ORDER OF STRING SUBSTITUTIONS
The above example illustrates that the order in which Ferret performs string substitutions and evaluates immediate mode expressions in the command line is significant. The successful evaluation of the embedded expression `($ppl$xlen)/2` requires that ($ppl$xlen) is evaluated before attempting the divide by 2 operation. The order of Ferret string substitutions is as follows:
1. substitute "GO" command arguments of the form "$1", "$2", ...
2. substitute symbols of the form ($symbol_name) (discussed here)
3. substitute command aliases
4. substitute immediate mode expressions. (But see example 3 below).
Example 1
If the script snoopy.jnl contains
DEFINE SYMBOL fcn = $1
DEFINE ALIAS ANSWER LIST/NOHEAD/FORMAT=("Result is
",$2)
ANSWER `($fcn)(($3^2)/2)`+5
then the command
yes? GO snoopy EXP F5.2 2.25
would evaluate to
DEFINE SYMBOL fcn = EXP
DEFINE ALIAS ANSWER LIST/NOHEAD/FORMAT=("Result
is ",F5.2)
LIST/NOHEAD/FORMAT=("Result is ",F5.2) `EXP((2.25^2)/2)`+5
and would result in Ferret output of "Result is 17.57."
Example 2
We can use grave accent syntax and string variables to substitute the string into the command line.
yes? LET my_reg = "X=0:180,Y=-40:40,L=1"
yes? SHADE sst[`my_reg`]
Example 3
Immediate mode substitution of a string variable may be used to set the values of qualifiers. However the region qualifiers (/X=/Y=etc.) on a command are used to set the context for the grave accent expression. So Ferret parses command qualifiers before it parses grave accent expressions. Thus we can use this syntax to set a region:
yes? let xreg = "40:180"
yes? let yreg = "60S:42S"
yes? set region/x=`xreg`/y=`yreg`
But including the qualifier name in the string variable is NOT valid (the qualifier is parsed BEFORE the grave accent expression is substituted, so Ferret would issue the error that `my_region` is an unknown qualifier).
yes? !THE FOLLOWING SYNTAX IS NOT VALID
yes? LET my_region = "x=40:180/y=-60:-42"; set region/`my_region`
Ch7 Sec7. CUSTOMIZING THE POSITION AND STYLE OF PLOT LABELS
All of the plot labels generated by Ferret are automatically defined as symbols. This includes the title ($labtit), X and Y axis labels ($labx),($laby), as well as the position labels (latitude, longitude, depth, time), which are normally placed at the upper left on a plot (see "Labels," p. 191). Sometimes it is desirable to change the location, size or fonts of these labels. The symbol facility makes it possible to do this in a way that is independent of the particular label strings or plot aspect ratio. See the demonstration script symbol_demo.jnl for an example.
Ch7 Sec8. USING SYMBOLS IN COMMAND FILES
Often in Ferret command files the identical argument substitutions must be repeated at several points in the file. Using symbols it is possible to write "cleaner" Ferret scripts in which the argument substitution occurs only onceto define a symbol which is used in place of the argument thereafter. See the demonstration script symbol_demo.jnl for an example.
Ch7 Sec9. PLOT+ STRING EDITING TOOLS
The PLOT+ program provides a variety of tools for editing symbol strings. See the PLOT+ Users Guide for further information (p. 546). The special functions manipulate and reformat character strings.
Note that many of these functions are handled directly by Ferret string functions such as STRINDEX, STRLEN, SUBSTRING, etc (p. 230).
The general format is SET sym $function(arg1, arg2,...). The functions are:
|
$EDIT(symbol,argument) |
Edit a symbol: change to uppercase, remove extra blanks, or remove all blanks |
|
$EXTRACT(start,length,symbol) |
Extracts selected characters from the input string. |
|
$INTEGER(symbol) |
Converts a number to integer format |
|
$LENGTH(symbol) |
Returns the length of the input string |
|
$LOCATE(substring,symbol) |
Locates a substring in the input string (compares only the first 30 characters of the substring.) |
|
$ELEMENT(position,delimiter,symbol) |
Extracts an element from an input string in which the elements are separated by a specified delimiter. |
Example:
yes? DEFINE SYMBOL test = my string
yes? PPL SET upper_test $EDIT(test,COMPRESS)
yes?
SHOW SYMBOL upper_test
UPPER_TEST = "my string"
Symbols may be edited and checked using the same controls that apply to journal file arguments.
The section of this users guide entitled "Arguments to GO tools" (p 25) describes the syntax for checking and editing arguments. The identical syntax applies to symbols. As with the GO tool arguments (e.g., "$4"), all string manipulations are case insensitive.
In brief, the capabilities include:
default strings
If a symbol is undefined a default value may be provided using the pattern
($my_symbol%my default string%). For example,
($SHAPE%XY%)
check against list of acceptable values
A list of acceptable string values may be provided using the pattern
($my_symbol%|option
1|option 2|%). For example,
($SHAPE%|X|Y|Z|T|%)
will ensure that only 1-dimensional shapes (X, Y, Z, or T) are acceptable.
string substitution
Any of the optional string matches provided can invoke a substitution using
the pattern ($my_symbol%|option 1>replacement|%). For example,
($SHAPE%|X>I|Y>J|Z>K|T>L|%)
will substitute I for X or J for Y, etc.
Asterisk ("*") provides default substitution
The asterisk character matches any string. For example,
($SHAPE%|X|Y|Z|T|*>other%)
will always result in "X," "Y," "Z," "T," or "other."
Asterisk ("*") provides limited string editing
The asterisk character, when used on the right hand side of a string substitution,
inserts the original symbol contents
($SHAPE%|*>The shape is *|%)
An error message can be provided if the symbol is undefined or doesn't
match any options. The pattern for this is
($my_symbol%|option 1|option
2|<error message text %). For example,
($SHAPE%|X|Y|Z|T|<Not a 1-dimensional shape%)
PPLUS defines a number of global symbols which are available to the user. They are documented in the PPLUS Users Guide, section 7.3, and listed in the chapter "Customizing Plots", section PPLUS special symbols (p.219 ).
There are a few symbols, generated automatically by plots, which are not documented in the PLOT PLUS for Ferret Users Guide. Those are shown like all symbols by SHOW SYMBOLS, but cannot be redefined by the user.
the number of pixels in the horizontal (X) and vertical (Y) size of the current Ferret output window. Note: these are "0" if there is no current window -- hence they can be used as a test of whether there is an open window.
gives "BIG" or "LITTLE" according to endianness of the CPU
FERRET_PLATFORM
give the Ferret version and the platform Ferret is running on.
gives the date and time when the current session began.
For help with Ferret see our Support Policy