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
|