DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 9.4 Searching for a Substring

9.4.1 Problem

You want to find a value within a string.

9.4.2 Solution

Use the indexOf( ) or lastIndexOf( ) methods.

9.4.3 Discussion

You can use the indexOf( ) and lastIndexOf( ) methods to determine whether a string contains a specified substring value. Each method returns the starting index of the substring if it is found. If the substring is not found, the value -1 is returned.

The indexOf( ) method takes up to two parameters:

substring

The substring value for which you want to search.

startIndex

The zero-relative starting position from which to search within the string. If undefined, the method begins the search from the beginning of the string.

If you want to test whether a string contains another string, you can use the indexOf( ) method with only one parameter. For example:

myString = "This string contains the word cool twice. Very cool!";

// Get the index of the first occurrence of the substring "cool" within myString. 
indexOfCool = myString.indexOf("cool");

// If the indexOf(  ) method returns -1, no occurrences of "cool" were found.
if (indexOfCool != -1) {
  // Displays: "String contains word cool at index 30", because the first occurrence
  // of the substring appears starting at index 30 within myString.
  trace("String contains word cool at index " + indexOfCool);
}

You can get the indexes of subsequent occurrences of a substring by specifying the second, optional parameter of the indexOf( ) method, StartIndex. A simple and effective way to search for the next occurrence of a substring is to pass the method a starting index parameter value of one more than what was returned by the previous search:

myString = "This string contains the word cool twice. Very cool!";

// Get the index of the first occurrence of the substring "cool" within myString. 
indexOfCool = myString.indexOf("cool");

if (indexOfCool != -1) {
  // Displays: "String contains word cool at index 30"
  trace("String contains word cool at index " + indexOfCool);
}

// Get the index of the second occurrence of the substring "cool" within myString. 
// Pass the method the previous value of indexOfCool + 1. This starts the search past
// the starting index of the previous occurrence, thus finding the next occurrence.
// If you do not add 1 to indexOfCool when passing it to indexOf(  ), the method
// returns the location of the first occurrence again.
indexOfCool = myString.indexOf("cool", indexOfCool + 1);

if (indexOfCool != -1) {
  // Displays: "String contains word cool at index 47", because the next occurrence
  // of the substring appears starting at index 47 within myString.
  trace("String contains word cool at index " + indexOfCool);
}

You can use indexOf( ) in a while statement to get the indexes of every occurrence of a substring. For example:

myString = "This string contains the word cool thrice. Very cool! Yes, cool!";

// Initialize indexOfCool to -1 so that the while statement searches the string from
// the 0 index (starting at indexOfCool + 1).
indexOfCool = -1;

// Loop until indexOf(  ) returns -1.
while ( (indexOfCool = myString.indexOf("cool", indexOfCool + 1)) != -1 ) {

  /* Displays:
     String contains word cool at index 30
     String contains word cool at index 48
     String contains word cool at index 59
  */
  trace("String contains word cool at index " + indexOfCool);
}

The while conditional in the preceding code looks more complicated than it really is. If you take it apart, you can see that it is composed of manageable and understandable parts. The first part assigns to indexOfCool the value returned by the indexOf( ) method:

indexOfCool = myString.indexOf("cool", indexOfCool + 1);

The first time that this statement is encountered, the initial value of indexOfCool is -1 because it was set to -1 prior to the while statement. This is necessary to start the search at index 0 because 1 is added to that value each time. If indexOfCool is undefined, the ActionScript interpreter resolves it to 0; adding 1 to 0 causes Flash to search the string starting at index 1 instead of 0, potentially missing a match if the search string occurs at the beginning of the searched string. Each subsequent time the while condition is evaluated, the value of indexOfCool has the value of the starting index of the previously found match.

The second part of the while conditional tests to make sure that the result of the indexOf( ) method is not -1. This is important because without it, the while loop would continue indefinitely. When indexOf( ) returns -1, it means that no more matches exist, so the while loop terminates.

You can also search backward in a string. The lastIndexOf( ) method always returns the starting index of the last occurrence of the substring (it returns the position of the beginning of the found string, not its end, even though the search is performed backward). Therefore, it takes only one parameter: the substring for which to search. If no match is found, lastIndexOf( ) returns -1.

myString = "This string contains the word cool twice. Very cool!";

// Get the index of the last occurrence of the substring "cool" within myString. 
indexOfCool = myString.lastIndexOf("cool");

if (indexOfCool != -1) {
  // Displays: "String contains word cool at index 47", because the last occurrence
  // of the substring appears starting at index 47 within myString.
  trace("String contains word cool at index " + indexOfCool);
}

Both the indexOf( ) and lastIndexOf( ) methods are case-sensitive in their searches. For example, the substring "cool" would not be found in the string "Cool" because the cases are not the same. To perform a case-insensitive search, use the toLowerCase( ) method in conjunction with the indexOf( ) or lastIndexOf( ) method:

// Create a string that spells "cool" as both "cool" and "Cool".
myString = "Cool! This is a string with the word cool. It spells cool as both ";
myString += "cool (lowercase) and Cool (capitalized).";

search = "cool";

// Output the index of the first occurrence of "cool". The result is 37 because it
// does not find "Cool" due to the case-sensitive search.
trace(myString.indexOf(search));

// Output the index of the first occurrence of "cool" after lowercasing the string
// with toLowerCase(  ). The result is 0 because it now finds "Cool" (which has been
// converted to "cool") at the beginning of the string.
trace(myString.toLowerCase(  ).indexOf(search));

// Output the index of the last occurrence of "cool". The result is 66 because it
// does not find the last "Cool" due to the case-sensitivity.
trace(myString.lastIndexOf("cool"));

// Output the index of the last occurrence of cool after lowercasing the string. The
// result is 87 because it finds the last "Cool".
trace(myString.toLowerCase(  ).lastIndexOf(search));

// Now change the search string to "Cool" (capitalized).
search = "Cool";

// Output the index of the first occurrence of "Cool" after lowercasing the string.
// The result is -1 because "Cool" doesn't exist in the lowercase string.
trace(myString.toLowerCase(  ).indexOf(search));

// This is similar to the preceding line of code, but the search string is also
// converted to lowercase. Therefore, the result is 0 because the starting index of
// the first occurrence of "cool" (regardless of case) is 0. Lowercasing both the
// string being searched and the substring for which you are searching ensures that a
// completely case-insensitive search is performed.
trace(myString.toLowerCase().indexOf(search.toLowerCase(  )));

You can use the indexOf( ) method to create a find feature for text fields:

// Include DrawingMethods.as from Chapter 4 for its drawRectangle(  ) method.
#include "DrawingMethods.as"

// Add the custom find(  ) method to TextField.prototype so that it is available to
// all text fields. The method takes three parameters: search is the search string,
// startIndex is the index from which to perform the search, and matchCase is a
// Boolean that specifies if the search should be case-sensitive. If matchCase is
// undefined, the search is case-insensitive.
TextField.prototype.find = function (search, startIndex, matchCase) {

  // Initialize the local variable, index.
  var index;

  // If matchCase is false or undefined, perform a case-insensitive search.
  // Otherwise, do a case-sensitive match.
  if (!matchCase) {
    index = this.text.toLowerCase().indexOf(search.toLowerCase(  ), startIndex);
  } else {
    index = this.text.indexOf(search, startIndex);
  }  

  // Set the focus to the text field in case it is not already set.
  Selection.setFocus(this);

  // If the search string was found, set the selection to highlight the match within
  // the text field. Otherwise, position the cursor at the beginning of the text
  // field.
  if (index != -1) {
    Selection.setSelection(index, index + search.length);
  } else {
    Selection.setSelection(0, 0);
  }

  // Return the index of the match so that it can be used to pass the next startIndex
  // value to this method when called again.
  return index;
};

// Example usage:

// Create a text field.
_root.createTextField("myText", 1, 100, 100, 0, 0);
myText.autoSize = true;
myText.border = true;
myText.text = "This is a text field with text. And more text.";

// Create a button movie clip.
_root.createEmptyMovieClip("myButton_mc", 2);
myButton_mc.lineStyle(0, 0x000000, 100);
myButton_mc.beginFill(0, 100);
myButton_mc.drawRectangle(100, 20);
myButton_mc.endFill(  );
myButton_mc._x = 400;
myButton_mc._y = 300;

// When the button is clicked, call the find(  ) method of the myText text field. Pass
// it the search string text. The myButton_mc.currentIndex is set with each call to
// find(  ) to the value of the starting index of the found match. Therefore, passing
// currentIndex + 1 to find(  ) makes it look for the next match.
myButton_mc.onRelease = function (  ) {
  this.currentIndex = _root.myText.find("text", this.currentIndex + 1, false);
};
    [ Team LiB ] Previous Section Next Section