Difference between revisions of "LlSubStringIndex"

From Second Life Wiki
Jump to navigation Jump to search
(Undo revision 76917 by Chaz Longstaff (Talk) You cannot post code from other wikis without permission from the content authors.)
m (Replaced old <LSL> block with <source lang="lsl2">)
 
(26 intermediate revisions by 9 users not shown)
Line 2: Line 2:
|func_id=181|func_sleep=0.0|func_energy=10.0
|func_id=181|func_sleep=0.0|func_energy=10.0
|func=llSubStringIndex
|func=llSubStringIndex
|return_type=integer|p1_type=string|p1_name=source|p2_type=string|p2_name=pattern
|return_type=integer
|func_footnote=If '''pattern''' is not found in '''source''', {{HoverText|-1|negative one, 0x{{LSL_Hex/Write|-1}}}} is returned.<br/>
|p1_type=string|p1_name=source|p1_desc=what to search in (haystack)
|p2_type=string|p2_name=pattern|p2_desc=what to search for (needle)
|func_footnote=If {{LSLP|pattern}} is not found in {{LSLP|source}}, -1 is returned.<br/>
The index of the first character in the string is {{HoverText|0|zero}}
The index of the first character in the string is {{HoverText|0|zero}}
|func_desc
|func_desc
|return_text=that is the index of '''pattern''' in '''source'''.
|return_text=that is the index of the first instance of {{LSLP|pattern}} in {{LSLP|source}}.
|spec
|spec
|caveats=*Performs a literal match (case sensitive).
|caveats=* Performs a literal match (case sensitive).
**Wildcards and RegEx are not supported.
** Wildcards and RegEx are not supported.
*Attempting to match an empty string ("") will return 0 instead of -1.
* If {{LSLP|pattern}} is an {{HoverText|empty string|{{String}}}} the value returned is {{HoverText|0|zero}} rather than -1.
*There is no function to search the string from the end or search starting at a specific offset.
* There is no function to search the string starting at a specific offset. check [[LlSubStringIndex#See_Also|See Also]] for a function to search from the end.
|constants
|constants
|examples=Matching against last names:
|examples=
<lsl>
<source lang="lsl2">
default
{
    touch_start(integer num_detected)
    {
        string  name      = llDetectedName(0);
        integer spaceIndex = llSubStringIndex(name, " ");
 
//      no conditional check is needed for (spaceIndex == -1)
//      because we KNOW the legacy name must have a space
 
//      extract the substring from the first character to the one before the space
        string  firstName  = llGetSubString(name, 0, spaceIndex - 1);
 
        llSay(PUBLIC_CHANNEL, firstName + " touched me.");
    }
}
</source>
<source lang="lsl2">
default
default
{
{
     state_entry()
     state_entry()
     {
     {
         llSensorRepeat("", NULL_KEY, AGENT, PI, 96.0, 20);
         llSensorRepeat("", NULL_KEY, AGENT_BY_LEGACY_NAME, PI, 96.0, 20);
     }
     }
   
 
     sensor(integer NumDet)
     sensor(integer num_detected)
     {
     {
        integer i;
    // Loop through all the sensor data and match against " Linden",  
       
    // this causes it to match with any last name of Linden (since there can't be spaces before the firstname)
        //Loop through all the sensor data and match against " Linden",  
    // Alternatively you could match a firstname with "FirstName " or anything else
        //this causes it to match with any last name of Linden (since there can't be spaces before the firstname)
 
        //Alternatively you could match a firstname with "FirstName "
         integer index;
         for(i = 0; i < NumDet; ++i)
        do
            if(~llSubStringIndex(llDetectedName(i), " Linden"))
        {
                 llInstantMessage(llDetectedKey(i), "Hello, I see you!");
            key avatarKey = llDetectedKey(index);
            string avatarLegacyName = llDetectedName(index);
 
        //  watch out for the bitwise-NOT (~)
        //  the next line translates to if (indexOfSearchedStringInOtherString != -1)
            integer isReallyALinden = ~llSubStringIndex(avatarLegacyName, " Linden");
 
            if (isReallyALinden)
                 llInstantMessage(avatarKey, "Hello, I see you!");
        }
        while (++index < num_detected);
     }
     }
}
}
</lsl>
</source>
 
'''Basic Example:'''
<source lang="lsl2">integer index = llSubStringIndex("string data","TEST");
if(index == -1) {
    llSay(PUBLIC_CHANNEL,"TEST was not found in the string");
} else {
    llSay(PUBLIC_CHANNEL,"TEST was found in the string.");
}</source>
=== String Cheese ===
 
<source lang="lsl2">//This example shows how you can ask if a word or group of words is in a given string.
//There is a limitation with this function. Your search of the string is for an exact match (case sensitive)
//so the string_example below would be hard to match.
 
string string_example = "ThIs serVes As aN exaMplE sTrinG. It ISn't toO coMPleX bUt HaS sOme mIlD vARietY";
 
//If you chat a question "Search for search_word" within range of the object this script is in
//it will recognize (by searching the chat msg) the "search for" part and take the word or words following it
//and check the string_example for those words.
 
string search_test_a = "seArCh foR";
 
//The example below works the same way but searches for the word in front of the recognized trigger question.
 
string search_test_b = "is the word I seek";
 
//Using this variable provides a way to manipulate the word(s) during the script without damaging the msg.
                 
string search_word;
 
// Provide a mnemonic for the -1 return code that means NOT FOUND
integer NOT_FOUND = -1;
 
default
{
    on_rez(integer param)//Although reseting the script on_rez provides many benefits
    { //in some cases it would be a bad idea because stored variables, lists and queued events would be trashed.
        llResetScript();
    }
    state_entry()
    {  //This is just for fun (but better to know what object is talking to you).
        llSetObjectName("String Cheese");
        llListen(PUBLIC_CHANNEL, "", llGetOwner(), "");//Listen to you on the public chat channel for everything you say.
    }
    listen(integer chan, string name, key id, string msg)
    {
        if(llSubStringIndex(llToUpper(msg), llToUpper(search_test_a)) != NOT_FOUND)
        {
            search_word = llStringTrim(llGetSubString(msg, llStringLength(search_test_a), -1), STRING_TRIM);
            if(llSubStringIndex(llToUpper(string_example), llToUpper(search_word)) != NOT_FOUND)
            {
                llSay(PUBLIC_CHANNEL, "I have found the word " + "''" + search_word + "''" + " in the example string");
            }
            else                       
            {
                llSay(PUBLIC_CHANNEL, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
            }
        }
        if(llSubStringIndex(msg, search_test_b) != NOT_FOUND)
        {
            search_word = llStringTrim(llGetSubString(msg, 0, (llSubStringIndex(msg, search_test_b)-1)), STRING_TRIM);
            if(llSubStringIndex(string_example, search_word) != NOT_FOUND)
            {
                llSay(PUBLIC_CHANNEL, "I have found the word " + "''" + search_word + "''" + " in the example string");
            }
            else
            {
                llSay(PUBLIC_CHANNEL, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
            }
        }
    }
}</source>
|helpers=
|helpers=
Tests to see if one string contains a copy of another:
Tests to see if one string contains a copy of another:
Line 40: Line 142:
1. Concise & conventional:
1. Concise & conventional:


<lsl>
<source lang="lsl2">
integer contains(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
integer contains(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
{
     return 0 <= llSubStringIndex(haystack, needle);
     return 0 <= llSubStringIndex(haystack, needle);
}
}
</lsl>
</source>


<lsl>
<source lang="lsl2">integer startswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
integer startswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
{
return llGetSubString(haystack, 0, llStringLength(needle) - 1) == needle;
    return llDeleteSubString(haystack, llStringLength(needle), 0x7FFFFFF0) == needle;
}
}</source>
</lsl>


<lsl>
<source lang="lsl2">integer endswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
integer endswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
{
return llGetSubString(haystack, -llStringLength(needle), -1) == needle;
    return llDeleteSubString(haystack, 0x8000000F, ~llStringLength(needle)) == needle;
}
}</source>
</lsl>


Note: Some of the snippets above return a result without ever calling llSubStringIndex.
Note: Some of the snippets above return a result without ever calling llSubStringIndex.
Line 65: Line 163:
2. Clever & smaller (calculates contains in ~54 bytes rather than ~60):
2. Clever & smaller (calculates contains in ~54 bytes rather than ~60):


<lsl>
<source lang="lsl2">integer contains(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
integer contains(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
{
     return ~llSubStringIndex(haystack, needle);
     return ~llSubStringIndex(haystack, needle);
}
}</source>
</lsl>


Note: The llSubStringIndex function returns -1 only when not found and the ~ operator returns zero only for -1, so the clever combination ~llSubStringIndex returns zero only for not found, else nonzero for found.
Note: The llSubStringIndex function returns -1 only when not found and the ~ operator returns zero only for -1, so the clever combination ~llSubStringIndex returns zero only for not found, else nonzero for found.
Line 78: Line 174:
{{LSL DefineRow||[[llListFindList]]|Find a list in another list}}
{{LSL DefineRow||[[llListFindList]]|Find a list in another list}}
{{LSL DefineRow||[[llGetSubString]]|Copy out part of a string of characters}}
{{LSL DefineRow||[[llGetSubString]]|Copy out part of a string of characters}}
{{LSL DefineRow||[[SubStringLastIndex|uSubStringLastIndex]]|Returns an integer that is the index of the {{LSLP|last}} pattern in source.}}
|also_tests
|also_tests
|also_events
|also_events

Latest revision as of 12:36, 22 January 2015

Summary

Function: integer llSubStringIndex( string source, string pattern );

Returns an integer that is the index of the first instance of pattern in source.

• string source what to search in (haystack)
• string pattern what to search for (needle)

If pattern is not found in source, -1 is returned.
The index of the first character in the string is 0

Caveats

  • Performs a literal match (case sensitive).
    • Wildcards and RegEx are not supported.
  • If pattern is an empty string the value returned is 0 rather than -1.
  • There is no function to search the string starting at a specific offset. check See Also for a function to search from the end.
All Issues ~ Search JIRA for related Bugs

Examples

default
{
    touch_start(integer num_detected)
    {
        string  name       = llDetectedName(0);
        integer spaceIndex = llSubStringIndex(name, " ");

//      no conditional check is needed for (spaceIndex == -1)
//      because we KNOW the legacy name must have a space

//      extract the substring from the first character to the one before the space
        string  firstName  = llGetSubString(name, 0, spaceIndex - 1);

        llSay(PUBLIC_CHANNEL, firstName + " touched me.");
    }
}
default
{
    state_entry()
    {
        llSensorRepeat("", NULL_KEY, AGENT_BY_LEGACY_NAME, PI, 96.0, 20);
    }

    sensor(integer num_detected)
    {
    //  Loop through all the sensor data and match against " Linden", 
    //  this causes it to match with any last name of Linden (since there can't be spaces before the firstname)
    //  Alternatively you could match a firstname with "FirstName " or anything else

        integer index;
        do
        {
            key avatarKey = llDetectedKey(index);
            string avatarLegacyName = llDetectedName(index);

        //  watch out for the bitwise-NOT (~)
        //  the next line translates to if (indexOfSearchedStringInOtherString != -1)
            integer isReallyALinden = ~llSubStringIndex(avatarLegacyName, " Linden");

            if (isReallyALinden)
                llInstantMessage(avatarKey, "Hello, I see you!");
        }
        while (++index < num_detected);
    }
}

Basic Example:

integer index = llSubStringIndex("string data","TEST");
if(index == -1) {
    llSay(PUBLIC_CHANNEL,"TEST was not found in the string");
} else {
    llSay(PUBLIC_CHANNEL,"TEST was found in the string.");
}

String Cheese

//This example shows how you can ask if a word or group of words is in a given string.
//There is a limitation with this function. Your search of the string is for an exact match (case sensitive)
//so the string_example below would be hard to match.

string string_example = "ThIs serVes As aN exaMplE sTrinG. It ISn't toO coMPleX bUt HaS sOme mIlD vARietY";

//If you chat a question "Search for search_word" within range of the object this script is in
//it will recognize (by searching the chat msg) the "search for" part and take the word or words following it
//and check the string_example for those words.

string search_test_a = "seArCh foR";

//The example below works the same way but searches for the word in front of the recognized trigger question.

string search_test_b = "is the word I seek";

//Using this variable provides a way to manipulate the word(s) during the script without damaging the msg.
                   
string search_word;

// Provide a mnemonic for the -1 return code that means NOT FOUND
integer NOT_FOUND = -1;

default
{
    on_rez(integer param)//Although reseting the script on_rez provides many benefits
    { //in some cases it would be a bad idea because stored variables, lists and queued events would be trashed.
        llResetScript();
    }
    state_entry()
    {   //This is just for fun (but better to know what object is talking to you).
        llSetObjectName("String Cheese");
        llListen(PUBLIC_CHANNEL, "", llGetOwner(), "");//Listen to you on the public chat channel for everything you say.
    }
    listen(integer chan, string name, key id, string msg)
    {
        if(llSubStringIndex(llToUpper(msg), llToUpper(search_test_a)) != NOT_FOUND)
        {
            search_word = llStringTrim(llGetSubString(msg, llStringLength(search_test_a), -1), STRING_TRIM);
            if(llSubStringIndex(llToUpper(string_example), llToUpper(search_word)) != NOT_FOUND)
            {
                llSay(PUBLIC_CHANNEL, "I have found the word " + "''" + search_word + "''" + " in the example string");
            }
            else                         
            {
                llSay(PUBLIC_CHANNEL, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
            }
        }
        if(llSubStringIndex(msg, search_test_b) != NOT_FOUND)
        {
            search_word = llStringTrim(llGetSubString(msg, 0, (llSubStringIndex(msg, search_test_b)-1)), STRING_TRIM);
            if(llSubStringIndex(string_example, search_word) != NOT_FOUND)
            {
                llSay(PUBLIC_CHANNEL, "I have found the word " + "''" + search_word + "''" + " in the example string");
            }
            else
            {
                llSay(PUBLIC_CHANNEL, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
            }
        }
    }
}

Useful Snippets

Tests to see if one string contains a copy of another:

1. Concise & conventional:

integer contains(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
    return 0 <= llSubStringIndex(haystack, needle);
}
integer startswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
    return llDeleteSubString(haystack, llStringLength(needle), 0x7FFFFFF0) == needle;
}
integer endswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
    return llDeleteSubString(haystack, 0x8000000F, ~llStringLength(needle)) == needle;
}

Note: Some of the snippets above return a result without ever calling llSubStringIndex.

2. Clever & smaller (calculates contains in ~54 bytes rather than ~60):

integer contains(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
    return ~llSubStringIndex(haystack, needle);
}

Note: The llSubStringIndex function returns -1 only when not found and the ~ operator returns zero only for -1, so the clever combination ~llSubStringIndex returns zero only for not found, else nonzero for found.

Note: Smaller was not noticeably faster or slower when our Code Racer and Efficiency Tester harnesses measured the expression { contains("wiki.secondlife.com", "wiki"); }.

See Also

Functions

•  llListFindList Find a list in another list
•  llGetSubString Copy out part of a string of characters
•  uSubStringLastIndex Returns an integer that is the index of the last pattern in source.

Deep Notes

Search JIRA for related Issues

Signature

function integer llSubStringIndex( string source, string pattern );