Difference between revisions of "LlSubStringIndex"

From Second Life Wiki
Jump to navigation Jump to search
m
(Erm...? Too much orange? Feel free to trim.)
Line 45: Line 45:
=== String Cheese ===
=== String Cheese ===


<lsl>//This example shows how you can ask if a word or group of words in in a given string
<lsl>//This example shows how you can ask if a word or group of words in 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.


string string_example = "ThIs serVes As aN exaMplE sTrinG. It ISn't toO coMPleX bUt HaS sOme mIlD vARietY";
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
//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
//it will recognize by searching the chat msg for the "search for" part and take the word or words following it
//by use of this function and check the string_example for those words
//and check the string_example for those words.


string search_test_a = "seArCh foR";
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
//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";
string search_test_b = "is the word I seek";
Line 68: Line 68:
{
{
     on_rez(integer param)//Although reseting the script on_rez provides many benefits
     on_rez(integer param)//Although reseting the script on_rez provides many benefits
     //in some cases it would be a bad idea cause stored varibles and lists would be trashed
     //in some cases it would be a bad idea cause stored varibles and lists would be trashed.
     {
     {
         llResetScript();
         llResetScript();
     }
     }
     state_entry()
     state_entry()
     {  //This is just for fun (better to know what object is talking to you though)
     {  //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(0, "", 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)
     {
     {
    //To work around the case sensitive nature of this function we can use llToUpper or llToLower
    //to convert all the strings to the same case. Since the return is a string there is no need to typecast.
    //Be sure to use the same (upper or lower) conversion.
    //The "if" test (question) below asks "is the index of the search_test_a not minus one?"
    //Although it seems like a strange question it makes perfect sense.
    //If the llSubStringIndex search gets to the end of the string (index -1) without finding search_test_a
    //it knows it isn't there.
         if(llSubStringIndex(llToUpper(msg), llToUpper(search_test_a)) != -1)
         if(llSubStringIndex(llToUpper(msg), llToUpper(search_test_a)) != -1)
         {
         {
    //We know the search_test_a word(s) is there so we need to take the word(s) that follow it out of the string
    //in order to search the string_example for it/them.llGetSubString returns the part of msg requested.
    //llStringLength tells us the index of the break between the search_test_a phrase and the words following it.
    //Since we know the words to be searched for are at the end of the msg we get the substring between the end
    //of search_test_a and the end of msg (-1) and then trim off any spaces we don't want and call it "search_word".
             search_word = llStringTrim(llGetSubString(msg, llStringLength(search_test_a), -1), STRING_TRIM);
             search_word = llStringTrim(llGetSubString(msg, llStringLength(search_test_a), -1), STRING_TRIM);
    //Search string_example for search_word.
             if(llSubStringIndex(llToUpper(string_example), llToUpper(search_word)) != -1)
             if(llSubStringIndex(llToUpper(string_example), llToUpper(search_word)) != -1)
             {
             {
                 llSay(0, "I have found the word " + "''" + search_word + "''" + " in the example string");
                 llSay(0, "I have found the word " + "''" + search_word + "''" + " in the example string");
             }
             }
             else                        
             else             //Say the results.           
             {
             {
                 llSay(0, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
                 llSay(0, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
             }
             }
         }
         }
    //With this search method an exact match (case sensitive) must be found.
         if(llSubStringIndex(msg, search_test_b) != -1)
         if(llSubStringIndex(msg, search_test_b) != -1)
         {
         {
    //This time the words we are searching for are taken from the beginning of the msg.
    //Almost the same method is used here but the indexes are reversed. We grab from zero index (start)
    //to the first instance of the search_test_b phrase minus 1 since the index is for the first letter of the
    //first word of that phrase (we don't want that bit).
             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);
    //Perform the search.
             if(llSubStringIndex(string_example, search_word) != -1)
             if(llSubStringIndex(string_example, search_word) != -1)
             {
             {
                 llSay(0, "I have found the word " + "''" + search_word + "''" + " in the example string");
                 llSay(0, "I have found the word " + "''" + search_word + "''" + " in the example string");
             }
             }
             else
             else             //Say the results
             {
             {
                 llSay(0, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");
                 llSay(0, "I cannot find the word " + "''" + search_word + "''" + " in the example string.");

Revision as of 15:12, 17 June 2009

Summary

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

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

• string source
• string pattern

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.
  • Attempting to match an empty string ("") will return 0 instead of -1.
  • There is no function to search the string from the end or search starting at a specific offset.
All Issues ~ Search JIRA for related Bugs

Examples

Matching against last names: <lsl> default {

   state_entry()
   {
       llSensorRepeat("", NULL_KEY, AGENT, PI, 96.0, 20);
   }
   
   sensor(integer NumDet)
   {
       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)
       //Alternatively you could match a firstname with "FirstName "
       for(i = 0; i < NumDet; ++i)
           if(~llSubStringIndex(llDetectedName(i), " Linden"))
               llInstantMessage(llDetectedKey(i), "Hello, I see you!");
   }

} </lsl>

Basic Example: <lsl>integer index = llSubStringIndex("string data","TEST"); if(index == -1) {

   llSay(0,"TEST was not found in the string");

} else {

   llSay(0,"TEST was found in the string.");

}</lsl>

String Cheese

<lsl>//This example shows how you can ask if a word or group of words in 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 for 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 info

string search_word;

default {

   on_rez(integer param)//Although reseting the script on_rez provides many benefits
   //in some cases it would be a bad idea cause stored varibles and lists 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(0, "", llGetOwner(), "");//Listen to you on the public chat channel for everything you say.
   }
   listen(integer chan, string name, key id, string msg)
   {
   //To work around the case sensitive nature of this function we can use llToUpper or llToLower
   //to convert all the strings to the same case. Since the return is a string there is no need to typecast.
   //Be sure to use the same (upper or lower) conversion.
   //The "if" test (question) below asks "is the index of the search_test_a not minus one?"
   //Although it seems like a strange question it makes perfect sense.
   //If the llSubStringIndex search gets to the end of the string (index -1) without finding search_test_a
   //it knows it isn't there.
       if(llSubStringIndex(llToUpper(msg), llToUpper(search_test_a)) != -1)
       {
   //We know the search_test_a word(s) is there so we need to take the word(s) that follow it out of the string
   //in order to search the string_example for it/them.llGetSubString returns the part of msg requested.
   //llStringLength tells us the index of the break between the search_test_a phrase and the words following it.
   //Since we know the words to be searched for are at the end of the msg we get the substring between the end
   //of search_test_a and the end of msg (-1) and then trim off any spaces we don't want and call it "search_word".
           search_word = llStringTrim(llGetSubString(msg, llStringLength(search_test_a), -1), STRING_TRIM);
   //Search string_example for search_word.
           if(llSubStringIndex(llToUpper(string_example), llToUpper(search_word)) != -1)
           {
               llSay(0, "I have found the word " + "" + search_word + "" + " in the example string");
           }
           else             //Say the results.            
           {
               llSay(0, "I cannot find the word " + "" + search_word + "" + " in the example string.");
           }
       }
   //With this search method an exact match (case sensitive) must be found.
       if(llSubStringIndex(msg, search_test_b) != -1)
       {
   //This time the words we are searching for are taken from the beginning of the msg.
   //Almost the same method is used here but the indexes are reversed. We grab from zero index (start)
   //to the first instance of the search_test_b phrase minus 1 since the index is for the first letter of the
   //first word of that phrase (we don't want that bit).
           search_word = llStringTrim(llGetSubString(msg, 0, (llSubStringIndex(msg, search_test_b)-1)), STRING_TRIM);
   //Perform the search.
           if(llSubStringIndex(string_example, search_word) != -1)
           {
               llSay(0, "I have found the word " + "" + search_word + "" + " in the example string");
           }
           else              //Say the results
           {
               llSay(0, "I cannot find the word " + "" + search_word + "" + " in the example string.");
           }
       }
   }
}</lsl>

Useful Snippets

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

1. Concise & conventional:

<lsl> integer contains(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex {

   return 0 <= llSubStringIndex(haystack, needle);

} </lsl>

<lsl>integer startswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex {

   return llDeleteSubString(haystack, llStringLength(needle), -1) == needle;

}</lsl>

<lsl>integer endswith(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex {

   return llDeleteSubString(haystack, 0, ~llStringLength(needle)) == needle;

}</lsl>

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

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

<lsl>integer contains(string haystack, string needle) // http://wiki.secondlife.com/wiki/llSubStringIndex {

   return ~llSubStringIndex(haystack, needle);

}</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: 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

Deep Notes

Search JIRA for related Issues

Signature

function integer llSubStringIndex( string source, string pattern );