Previous Page
Next Page

Recipe 12.4. Searching for a Substring

Problem

You want to find a string value inside of a string.

Solution

Use the indexOf( ) or lastIndexOf( ) methods from the String class.

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 found. The indexOf( ) method searches from left to right, whereas the lastIndexOf( ) methods searches from right to left in the string. If the substring is not found, the value -1 is returned.

The indexOf( ) method takes two parameters:



substring

The substring value for which you want to search.



startIndex

The optional zero-based starting position from which to search within the string. Zero-based means the first character in the string is at position 0, not 1. If omitted, the method begins the search from the beginning of the string (at index 0).

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

var example:String = "This string contains the word cool twice. Very cool.";

// Get the index of the first occurrence of the substring "cool" within example. 
var index:int = example.indexOf( "cool" );

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

You can get the indices of subsequent occurrences of a substring by specifying the second, optional parameter of the indexOf( ) method. 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.

var example:String = "This string contains the word cool twice. Very cool.";

// Get the index of the first occurrence of the substring "cool" within example. 
var index:int = example.indexOf( "cool" );

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

// Get the index of the second occurrence of the substring "cool" within example.
// Pass the method the previous value of index + 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 index when passing it to
// indexOf(  ), the method returns the location of the first occurrence again.
index = example.indexOf( "cool", index + 1 );

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

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

var example:String = "This string contains the word cool. Very cool. Yes, cool.";

// Initialize index to 1 so that the while statement searches 
// the string from the beginning (index 0, at index + 1)
var index:int =  -1;

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

  /* Displays:
     String contains word cool at index 30
     String contains word cool at index 41
     String contains word cool at index 52
  */
  trace( "String contains word cool at index " + index );
}

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 index the value returned by the indexOf( ) method:

index = example.indexOf( "cool", index + 1 );

The first time this statement is encountered, the initial value of index is -1 because it was set to -1 prior to the while statement. It is absolutely essential that this be done for the process to work properly, since 1 is added to the index each loop iteration. If index is not given a value, a default value of 0 is used; adding 1 to 0 causes the search to start at the second character of the string, at index 1. Each subsequent time the while condition is evaluated, the value of index has the value of the starting index of the previously found match. If we fail to add 1 to index in the loop, the index never moves past the first match and loops infinitely. That is, if the indexOf( ) method returns 30 and it starts looking from 30 on the next iteration, 30 is returned again by indexOf( ) and the cycle repeats.

The second part of the while conditional tests to make sure that the result of the indexOf( ) method is not -1 before executing the loop body again. This is important because without it the while loop would continue infinitely since any non-zero numerical value, positive or negative, in a conditional is interpreted as TRue. When indexOf( ) returns -1, it means that no more matches exist, so the while loop terminates.

The lastIndexOf( ) method works much like indexOf( ), except that it searches from right to left in the string, effectively searching the string backward. The lastIndexOf( ) method returns the starting index of the last occurrence of the substring. The starting index returned is the position of the beginning of the found substring, not its end, even though the search is performed backward.

Like indexOf( ), the lastIndexOf( ) method takes two parameters:



substring

The substring value for which you want to search.



startIndex

The optional zero-based starting position from which to search within the string. Zero-based means the first character in the string is at position 0, not 1. If omitted the method begins the search from the end of the string (at index string.length 1).

When the substring cannot be found in the string, lastIndexOf( ) returns -1:

var example:String = "This string contains the word cool twice. Very cool.";

// Get the index of the last occurrence of the substring "cool" within example. 
var index:int =  example.lastIndexOf( "cool" );

if ( index != -1 ) {
  // Displays: "String contains word cool at index 47" because the last 
  // occurrence of the substring appears starting at index 47 within example.
  trace( "String contains word cool at index " + index );
}
// Get the index of the next to last occurrence of "cool" within example.
// Pass the method the previous value of index - 1. This starts the
// search one to the left original occurrence, thus finding an earlier occurrence
// from right to left. If you  do not subtract 1 to index when passing it to
// lastIndexOf(  ), the method returns the location of the last occurrence again.
index = example.lastIndexOf( "cool", index - 1 );

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

If you want to retrieve the indices of every occurrence of a substring, you can use the same technique we previously used for indexOf( ), a combination of a while loop and lastIndexOf( ) with a starting index value.

var example:String = "This string contains the word cool. Very cool. Yes, cool.";

// Initialize index to example.length so that the while statement starts searching
// at the very last index. The last index is actually example.length  1, but we
// subtract 1 from the index in the loop so we initialize index carefully here.
var index:int =  example.length;

// Loop until lastIndexOf(  ) returns 1.
while ( ( index = example.lastIndexOf( "cool", index - 1 ) ) != -1 ) {

  /* Displays:
     String contains word cool at index 52
     String contains word cool at index 41
     String contains word cool at index 30
  */
  trace( "String contains word cool at index " + index );
}

This code block is very similar to the one used for indexOf( ) but there are two subtle differences. The first difference is that we initialize index to example.length instead of -1 as before because lastIndexOf( ) searches from right to left instead of left to right and we need to start our search at the end of the example string. The second difference is that instead of adding 1 to the index for the search, we subtract 1 so that lastIndexOf( ) will search to the left of a previously found occurrence to find all occurrences.

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".
var example:String = "Cool. This is a string with the word cool. It spells"
                   + " cool as both cool (lowercase) and Cool (capitalized).";

var search:String = "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( example.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( example.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( example.lastIndexOf( search ) );

// Output the index of the last occurrence of cool after lowercasing the
// string. The result is 87, because it finds the last "Cool".
trace( example.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( example.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( example.toLowerCase(  ).indexOf( search.toLowerCase(  ) ) );

In this code snippet, you can freely substitute toUpperCase( ) for toLowerCase( ) for case-insensitive searching. The key is that you force the original string and the substring to the same case (either upper or lower) before starting the search.

See Also

Recipe 13.3


Previous Page
Next Page
Converted from CHM to HTML with chm2web Pro 2.85 (unicode)