String Functions Updated: Aug 28, 2008 Return string result r$ based on applicable numeric or string arguments. Arguments are described as "string", "number" (any type), "integer" (INTEGER, LONG, DWORD) and "immediate" number like 0, 1, etc. STRING functions can be used on binary data. Please see notes below. NUMERIC functions with string arguments include: ASC, DIREXISTS, FILEEXISTS, HEX2DW, INSTR, LEN, MESSAGEBOX, TALLY and VAL. Function Arguments, Comments, Examples ~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [] Square brackets are used to get a single byte from a string. r$ = MyString[i] 'i = 1 to MyString.Length ANSI$ 8-bit encoded string from wide 16-bit encoded string r$ = ANSI$(MyWideString) ARRAYREF$ + Reads string array value based on a pointer to the array object and the element subscripts. r$ = ARRAYREF$(lpArrayObject, subscripts) where lpArrayObject is obtained with the OBJPTR() Numeric Function and subscripts is a comma-delimited list of numeric values as might be used in reading a conventional array value. DIM A(99,99) As STRING*32 DEFINT Aptr = OBJPTR(A) DEFINT i, j 'code to set values for i and j r$ = ARRAYREF$(Aptr,i,j) 'same as r$ = A(i,j) Notice you can pass an OBJPTR() value as an argument to a procedure which may read/write array values based on a pointer, not the dimensioned name of the array. Please see the ARRAYREF$() statement in Advanced Techniques. BIN$ String of 1s and 0s for integer; r$ = BIN$(integer) BYREF$ Alias for VARPTR$ described below CHR$ One-byte ASCII string for integer 0 - 255; r$ = CHR$(integer) COMMA Comma string; same as ","; r$ = COMMA: PRINT s$; COMMA; COMMAND$ Command line item; r$ = COMMAND$(1) is first argument; r$ = COMMAND$(0) 'get application pathname. The command line may contain arguments as quoted strings with embedded spaces like "C:\Program Files\". COMMANDLINE$ Command line; r$ = COMMANDLINE$ 'entire command line CRLF CR and LF chars = CHR$(13) + CHR$(10) but faster; r$ = s$ + CRLF CURDIR$ Current working directory; r$ = CURDIR$ DATE$ Date as string; r$ = DATE$ DELETE$ Deletes n characters starting at position i in string; r$ = DELETE$(string, i, n) 'i and n are integer expressions If string.Length < i, then r$ = string DIR$ First file with pathname mask string and attributes integer; r$ = DIR$(string, integer) r$ = Dir$("*.wav",0) 'gets first .wav file in current directory Goto DoWavFile NextWavFile: r$ = Dir$ 'gets next .wav file if any DoWavFile: IF r$.Length THEN Call ProcessThisFile: Goto NextWavFile Note: With attributes integer 0 in the example above, all entries are returned including those with no attribute bits set. The attributes argument can be used to get volume label (&H1000), subdirectories (&10), read only (&H1), hidden (&H2), system (&H4), archive (&H20), normal (&H80), temporary (&H100), compressed (&H800) or any combination (by summing the hex flags). Example: Attribute mask &H27 returns ordinary files only. Linux: DIR$() does not use a "file mask" string, only a path. Example: $IFDEF LINUX $define slash "/" 'Linux $define DIR_FLAG &H4000 $ELSE $define slash "\" 'Windows $define DIR_FLAG &H10 $ENDIF DEFINT IsDir 'code IF p$[p$.Length]<>slash THEN p$.append slash 'path has trailing slash $IFNDEF LINUX p$.append "*.*" 'use file mask only on Windows $ENDIF r$ = Dir$(p$,0) Notice "*.*" is used only in the Windows version; Linux version uses the path only with trailing slash. We can filter files and sub-directories: next_dir_item: IF r$.Length then IF r$[r$.Length]="." THEN Goto next_dir_item 'reject these IF FILEREC.ATTR AND DIR_FLAG THEN r$.append slash: IsDir=1 ELSE IsDir=zero END IF 'etc END IF determines if the item is a file (IsDir = 0) or directory (IsDir = 1). ENVIRON$ Gets environment variable value, r$ = ENVIRON$(string) IF ENVIRON$("REQUEST_METHOD")="POST" THEN k=VAL(ENVIRON$("CONTENT_LENGTH")) IF k<=0 THEN PRINT "Status: 400 No Content Posted": PRINT: Goto ProgEnd IF k>4096 THEN k=4096 'to protect from excessive content length q$=GET$(k) ELSE q$=ENVIRON$("QUERY_STRING") 'if you expect one END IF FIELD$ Gets field integer n (1 to ??) of string1 using delimiter string2 r$ = FIELD$(string1, string2, n) 'r$ = "" if field n does not exist DIM s$ As LIST: r$ = FIELD$(s$, CRLF, 5) 'same as r$ = s$.Item(4) Note: FIELD$ can be used on binary data as long as string2 uniquely marks fields. E.g., if "NEXTIMAGE" never occurs in an image, then let's have some fun to show the kinds of things that can be done. DIM bmpsize As LONG, newbmp As STRING DIM bmp As STRING, imagelist As STRING 'or As MEMORY imagelist.LoadFromFile("MyGame.wad") 'MyGame soon to be top video game! bmp = FIELD$(imagelist, "NEXTIMAGE", 10) 'retrieves image 10 to bmp bmpsize = bmp.Length 'just in case we want to know how many bytes it is IF bmpsize THEN PRINT "Yes, there is an image 10 there; our hero jumping." newbmp.LoadFromFile("new.bmp") IF INSTR(newbmp, "NEXTIMAGE") THEN PRINT "Problems! Delimiter in image!" imagelist = imagelist + newbmp + "NEXTIMAGE" 'add an image to the list GET$ Get integer n characters from StdIn (or keyboard) without echo to StdOut (or screen); r$ = GET$(integer). Note: CGI programs use GET$. GET$ is blocking; that is, control returns to program after n characters are received. For CGI applications, if the server is worth its salt; it will send the characters. CGI programs should not ask for more characters than the server indicates are available with CONTENT_LENGTH. GETGTK$ Gets string value for property of FORM object by handle r$ = GETGTK$(handle, string property) Linux GTK-mode only; example: r$ = GETGTK$(myEdit,"text") HEX$ Hex string for integer number; r$ = HEX$(integer) IIF$ + Evaluates expression and returns either string1 (true) or string2 (false). r$ = IFF$(expression, string1, string2) r$ = IIF$(2 > 1, "TRUE", "FALSE") 'returns "TRUE" r$ = IIF$(a$ > b$, f1$, f2$) Note: There is an IIF numeric function (please see Numeric Functions). INKEY$ Check for keyboard character and return immediately; r$ = INKEY$ DO 'code SLEEP 0.1 'You use 100% CPU time without the sleep; don't push your luck! r$ = INKEY$ LOOP UNTIL r$ <> NULL INPUT$ Input a keyboard string without a prompt; r$ = INPUT$ Note: for keyboard input of SQL commands which might contain embedded "," characters, use the INPUT$ function rather than the INPUT statement which parses the input line as a comma-delimited list of values. INSERT$ Inserts string2 into string1 at integer position n; r$ = INSERT$(string1, string2, n) If string1.Length < n, then string2 is appended to string1. LCASE$ Lower case; r$ = LCASE$(string) LEFT$ First integer n characters; r$ = LEFT$(string, n) LTRIM$ Remove leading spaces; r$ = LTRIM$(string) MID$ Extract substring at integer position p of optional integer length n; r$ = MID$(string, p, n) 'returns n bytes of string starting at p r$ = MID$(string, p) 'returns remainder of string starting at p Note: can be used to extract fields from STRING or MEMORY binary data. NULL Null string of zero length (""); r$ = NULL Note: IF r$ <> NULL THEN ... 'Note IF r$.len THEN ... is faster PATH$ Path of application including trailing "\", derived from COMMAND$(0). CHDIR PATH$ 'set application directory as working directory. Same as APP.Path QUOTE Double quote string; r$ = QUOTE + text$ + QUOTE REPLACE$ In string1, replaces data with string2 at integer position n; r$ = REPLACE$(string1, string2, n) If string1.Length < n, then r$ = string1 REPLACESUBSTR$ In string1, replaces all instances of string2 with string3; r$ = REPLACESUBSTR$(string1, string2, string3) Let's say you want multi-line text items in a LIST but CRLF is the delimiter used by the LIST object. Try, DIM MayBeMultiLine As STRING, MyList As LIST 'code to get data r$ = REPLACESUBSTR$(MayBeMultiLine, CRLF, "#") 'I like "#" but CHR$(0) is flawless! MyList.AddItems r$ 'later in code r$ = MyList.Item(n) 'now we need this item r$ = REPLACESUBSTR$(r$, "#", CRLF) 'Now r$ is back to multi-line text REVERSE$ Reverse order of string bytes; r$ = REVERSE$(string) 'implement RINSTR like this from hotreg.bas: IF key$<>NULL THEN r$=REVERSE$(key$): i=INSTR(r$,"\") IF i THEN j=key$.length: key$=LEFT$(key$,j-i) ELSE key$=NULL END IF END IF Note: The RINSTR function is in HotInclude as RINSTR.inc. RIGHT$ Last integer n characters; r$ = RIGHT$(string, n) RTRIM$ Remove trailing spaces; r$ = RTRIM$(string) SPACE Space character = " "; r$ = SPACE: PRINT x; SPACE; SPACE$ String of integer n space characters; r$ = SPACE$(n) STR$ String representation of number; r$ = STR$(number): PRINT r$ STR$ is 3 different functions for unsigned non-float, signed non-float and float. If a variable is the argument, its type selects which conversion mode is used. If the argument is an expression, the compiler may choose float mode (and you may get "0.999999..." instead of "1"). If and only if the number is floating and the FPU is in an error state, then "ERROR" is returned by STR$. Thus, prior to use of STR$ with a floating argument, one can use FPU.Init or IF FPU.Error THEN FPU.Init. For signed and unsigned non-floating variables, the FPU is not used and a valid string conversion should always be returned by STR$. STRING$ String of integer n characters of string; r$ = STRING$(n, string) r$ = STRING$(20, ".") 'twenty dots r$ = STRING$(256, CHR$(0)) '256 character buffer of 0 bytes TAB Tab string; same as CHR$(9); r$ = TAB: PRINT s$; TAB; t$ TIME$ Local time; r$ = TIME$ TRIM$ RTRIM$ and LTRIM$; r$=TRIM$(r$) 'remove leading and trailing spaces UCASE$ Upper case; r$ = UCASE$(string) Note: RapidQ appears to use "atom" APIs for LISTs and comparisons for duplicates are not case-sensitive. In HotBasic, LIST lookups are case-sensitive (HotBasic does all its own LIST functions). Thus, for non-case-sensitive LIST lookups, try DIM MyList As LIST MyList = UCASE$(MyList) 'Now let's avoid duplicates by a lookup before adding items IF MyList.IndexOf(r$) < 0 THEN MyList.AddItems r$ '.IndexOf = -1 if r$ absent VARIANTREF$ Accesses VARIANT data by pointer; r$ = VARIANTREF$(integer) where integer is a pointer to a VARIANT. VariantRef$() reads VARIANT data by reference, useful when functions in other modules may provide a result as a pointer to a VARIANT. VARPTR$ String at integer address (alias BYREF$); r$ = VARPTR$(integer) WIDE$ 16-bit encoded string from 8-bit encoded string r$ = WIDE$(My8BitString) WINDIR$ Windows directory; r$ = WINDIR$ WINDOW + Gets FORM object string property by object handle. r$ = WINDOW(handle).Caption 'get property Syntax: WINDOW(handle[,qualified_type]).property The optional qualified_type argument is needed in special cases where the compiler needs this information to generate special code in your application. If handle alone does not work, add the qualified type. r$ = WINDOW(handle, GRID).Cell(i,j) ########### Generally, the string functions are not "text dependent". That is, fields in binary data can be extracted (MID$), inserted, deleted, replaced, etc, with string functions. E.g., DIM M As MEMORY, i As DWORD M = MID$(binary$,8,4): M.Position = 0: i = M.ReadNum(4) extracts 4 bytes of binary data at position 8 from binary$ and assigns the value to DWORD variable i. For a single byte, i = ASC(binary$[j]) where j = offset + 1. The foregoing may be cute, but is it sexy? To be a HotBasic stud or babe, try this one-liner: MEMCPY @i, @binary$+8, 4 'extracts 4 bytes at offset 8 to DWORD var i Sounds like the kind of thing one might do in extracting fields from raw network packets. For network protocols, packet data may consist of values of different length, identified only by position. So an ARRAY with fixed item length is troublesome. MID$, however, does it all. The other string functions can also be looked at in this light for perhaps novel uses, taking advantage of the "low level" treatment of string data built into HotBasic. For situations like this, one can also define a TYPE where the "fields" in a binary string are listed. Then, for example, TYPE IP_HEADER 'ip header items named and associated with a data length: byte, word, etc DestIP As DWORD 'etc END IP_HEADER Dim header as IP_HEADER, sock as SOCKET, recv$ As STRING 'set up other items to read a data packet into recv$, then, header = LEFT$(recv$,20) 'assuming header length of 20 bytes DestIP$ = sock.AddrToStr(header.DestIP) 'etc ... well, you get the idea. COMMAND$(), COMMANDLINE$, DATE$, ENVIRON$, TIME$, etc. win32.hlp states that string pointers obtained from the OS do not have permanence and that the data should be copied. HotBasic copies such data to application temporary buffers. However, such strings still do not have permanence for use as arguments in other functions which may use the same buffers. Thus, for complete reliability, strings from the OS should be assigned to user-dimensioned variables. E.g., MyDate$=DATE$. Then, use MyDate$ as an argument. + Penthouse (registered) version Copyright 2003-2007 James J Keene PhD Original Publication: Oct 8, 2003