rubyguides

String#include?

String#include? checks whether a substring exists within a string. It returns a boolean value, making it straightforward for conditional logic throughout your Ruby code. This method has been available since Ruby 1.8, making it one of the oldest and most reliable string methods in the language.

Key takeaways

  • include? is the simplest way to ask whether one text contains another.
  • It performs case-sensitive character matching.
  • Use it for quick checks, file extension tests, and validation rules.
  • Convert both sides to the same case when you want case-insensitive matching.
  • Reach for a regex if you need a more flexible search than a literal substring.

Signature

string.include?(other_str) -> true or false
ParameterTypeDescription
other_strStringThe substring to search for

The method takes a single argument: the substring you want to find. It does not accept a regular expression, so the search is always a literal match. This makes include? predictable and easy to reason about when you need a straightforward yes-or-no answer.

Code Examples

Basic substring check

text = "Hello, World!"

text.include?("Hello")   # => true
text.include?("World")   # => true
text.include?("Ruby")   # => false

The output is a simple boolean, so you can drop include? directly into an if condition or use it as a guard clause. The check is straightforward: either the substring is present somewhere in the text, or it is not.

Case-sensitive matching

email = "user@example.com"

email.include?("example")   # => true
email.include?("EXAMPLE")  # => false (case matters)
email.include?("@")         # => true

This is important to remember: include? performs exact character matching. If you need case-insensitive checking, convert both strings to the same case first. The normalization step is a small but essential detail because Ruby treats uppercase and lowercase characters as completely distinct, even for the same letter.

email.upcase.include?("EXAMPLE")  # => true when email is "user@example.com"

Normalizing to the same case before calling include? is the standard approach when the search should be case-insensitive. Calling upcase on the receiver creates a temporary uppercase copy, which is fine for short strings but worth keeping in mind for very large texts where you might want to downcase instead.

Using in conditional statements

filename = "report.pdf"

if filename.include?(".pdf")
  puts "Processing PDF file"
  # Output: Processing PDF file
end

if filename.include?(".doc")
  puts "Processing Word document"
  # No output - condition is false
end

This pattern is common for file type detection. You can chain multiple checks for more complex logic, but keep in mind that each include? call scans the entire string from the beginning, so three checks on the same text means three full scans.

Checking for multiple substrings

message = "Please contact support@example.com for help"

message.include?("contact")   # => true
message.include?("http")      # => false
message.include?("@")         # => true

When checking for several substrings at once, the calls read naturally as a series of yes-or-no questions. Each one is independent, so the order does not matter. For a large set of patterns, however, a regular expression with alternation is more efficient than repeated include? calls.

Practical validation example

def valid_username?(username)
  return false if username.include?(" ")
  return false if username.include?("@")
  username.length >= 3
end

valid_username?("john_doe")   # => true
valid_username?("john doe")   # => false (contains space)
valid_username?("ab")         # => false (too short)

Return Value

Returns true if the text contains the given substring anywhere within it, false otherwise. The search is case-sensitive, meaning “A” and “a” are considered different characters. The method stops searching as soon as it finds a match, so performance is proportional to the length of the text being searched.

Edge Cases

Empty string

"".include?("")    # => true (empty string is always found)
"hello".include?("")   # => true

This behavior follows the mathematical definition where an empty set is a subset of all sets. While technically true, checking for an empty substring is rarely useful in practice. The result makes sense in set theory but is almost never the question a program actually intends to ask.

Nil argument

"hello".include?(nil)
# => NoMethodError: undefined method `include?' for "hello":String

Passing nil raises a NoMethodError because nil does not have a to_s conversion in this context. Always ensure your argument is a string before calling include?. A quick guard like return false if other_str.nil? or other_str.to_s can prevent the error, but be aware that nil.to_s returns an empty string which always matches.

Special characters

"price: $50".include?("$")   # => true
"a\tb".include?("\t")        # => true (tabs are matched)
"line1\nline2".include?("\n") # => true (newlines are matched)

The method handles all ASCII and Unicode characters correctly, including whitespace, punctuation, and special symbols.

Common mistakes

One common mistake is forgetting that include? is case-sensitive. If you want “Example” and “example” to match, normalize the strings before checking.

Another mistake is using include? for something more complex than a literal substring search. If you need to match optional spacing, punctuation, or character classes, a regular expression will usually be clearer.

Conclusion

String#include? stays useful because it does one thing clearly: it answers whether a string contains another string. Keep the check literal, keep the case rule in mind, and use a regex only when the search needs more flexibility.

Single character vs multi-character

"hello".include?("l")    # => true (single character works)
"hello".include?("ll")   # => true (multiple characters work)
"hello".include?("lo")  # => true (adjacent characters work)

Performance Notes

For very large texts, include? performs a linear search. If you need to check for many different substrings repeatedly, consider using a Set or a Trie data structure instead. For most everyday use cases, the performance is more than adequate.

See Also