Difference between revisions of "LlSubStringIndex"

From Second Life Wiki
Jump to navigation Jump to search
m
m (Replaced old <LSL> block with <source lang="lsl2">)
 
(8 intermediate revisions by 4 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 {{LSLP|pattern}} is not found in {{LSLP|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
Line 10: Line 12:
|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.
* if ({{LSLPT|pattern}} == ""), 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 starting at a specific offset. check [[LlSubStringIndex#See_Also|See Also]] for a function to search from the end.
* 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:'''
'''Basic Example:'''
<lsl>integer index = llSubStringIndex("string data","TEST");
<source lang="lsl2">integer index = llSubStringIndex("string data","TEST");
if(index == -1) {
if(index == -1) {
     llSay(0,"TEST was not found in the string");
     llSay(PUBLIC_CHANNEL,"TEST was not found in the string");
} else {
} else {
     llSay(0,"TEST was found in the string.");
     llSay(PUBLIC_CHANNEL,"TEST was found in the string.");
}</lsl>
}</source>
=== String Cheese ===
=== String Cheese ===


<lsl>//This example shows how you can ask if a word or group of words is in a given string.
<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)
//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.
//so the string_example below would be hard to match.
Line 64: Line 94:
                    
                    
string search_word;
string search_word;
// Provide a mnemonic for the -1 return code that means NOT FOUND
integer NOT_FOUND = -1;


default
default
Line 74: Line 107:
     {  //This is just for fun (but better to know what object is talking to you).
     {  //This is just for fun (but better to know what object is talking to you).
         llSetObjectName("String Cheese");
         llSetObjectName("String Cheese");
         llListen(0, "", llGetOwner(), "");//Listen to you on the public chat channel for everything you say.
         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)
     listen(integer chan, string name, key id, string msg)
     {
     {
         if(llSubStringIndex(llToUpper(msg), llToUpper(search_test_a)) != -1)
         if(llSubStringIndex(llToUpper(msg), llToUpper(search_test_a)) != NOT_FOUND)
         {
         {
             search_word = llStringTrim(llGetSubString(msg, llStringLength(search_test_a), -1), STRING_TRIM);
             search_word = llStringTrim(llGetSubString(msg, llStringLength(search_test_a), -1), STRING_TRIM);
             if(llSubStringIndex(llToUpper(string_example), llToUpper(search_word)) != -1)
             if(llSubStringIndex(llToUpper(string_example), llToUpper(search_word)) != NOT_FOUND)
             {
             {
                 llSay(0, "I have found the word " + "''" + search_word + "''" + " in the example string");
                 llSay(PUBLIC_CHANNEL, "I have found the word " + "''" + search_word + "''" + " in the example string");
             }
             }
             else                         
             else                         
             {
             {
                 llSay(0, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
                 llSay(PUBLIC_CHANNEL, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
             }
             }
         }
         }
         if(llSubStringIndex(msg, search_test_b) != -1)
         if(llSubStringIndex(msg, search_test_b) != NOT_FOUND)
         {
         {
             search_word = llStringTrim(llGetSubString(msg, 0, (llSubStringIndex(msg, search_test_b)-1)), STRING_TRIM);
             search_word = llStringTrim(llGetSubString(msg, 0, (llSubStringIndex(msg, search_test_b)-1)), STRING_TRIM);
             if(llSubStringIndex(string_example, search_word) != -1)
             if(llSubStringIndex(string_example, search_word) != NOT_FOUND)
             {
             {
                 llSay(0, "I have found the word " + "''" + search_word + "''" + " in the example string");
                 llSay(PUBLIC_CHANNEL, "I have found the word " + "''" + search_word + "''" + " in the example string");
             }
             }
             else
             else
             {
             {
                 llSay(0, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
                 llSay(PUBLIC_CHANNEL, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
             }
             }
         }
         }
     }
     }
}</lsl>
}</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 109: 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>integer startswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
<source lang="lsl2">integer startswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex
{
{
     return llDeleteSubString(haystack, llStringLength(needle), -1) == needle;
     return llDeleteSubString(haystack, llStringLength(needle), 0x7FFFFFF0) == needle;
}</lsl>
}</source>


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


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


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.

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 );