Skip to content

Ruby Regular Expressions

A regular expression (regex) is a sequence of characters that defines a search pattern. In Ruby, regular expressions are objects of the Regexp class and are widely used for string searching, matching, and manipulation.

You can create Regexp objects using a literal form /pattern/ or %r{pattern} (useful if your pattern contains many slashes), or by using Regexp.new("pattern").

/your_pattern_here/
/your_pattern_here/im # 'i' for case-insensitive, 'm' for multiline
%r{another_pattern} # General delimited regular expression, good for patterns with slashes
Regexp.new("dynamic_pattern")

The =~ operator checks if a string matches a regex, returning the starting index of the match or nil if no match is found. The String#match method is often more useful as it returns a MatchData object containing detailed information about the match (including captures) or nil.

#!/usr/bin/env ruby
text1 = "Ruby is a fun language."
text2 = "Python is also cool."
# Using =~ operator
if text1 =~ /Ruby/
puts "text1 contains 'Ruby' (using =~)"
end
# Using String#match
match_data = text1.match(/fun (\w+)/) # (\w+) is a capturing group
if match_data
puts "text1 contains 'fun language' (using match)"
puts "The matched word after 'fun': #{match_data[1]}" # Accessing the first capture group
puts "Full match: #{match_data[0]}"
end
if text2.match(/Ruby/)
puts "text2 contains 'Ruby'"
else
puts "text2 does not contain 'Ruby'"
end

This will produce the following result:

text1 contains 'Ruby' (using =~)
text1 contains 'fun language' (using match)
The matched word after 'fun': language
Full match: fun language
text2 does not contain 'Ruby'

Modifiers change how a regex pattern is interpreted. They are placed after the closing delimiter of a literal regex (e.g., /pattern/i).

ModifierDescription
iCase-insensitive: Ignores case when matching.
mMultiline mode: The dot (.) metacharacter matches newline characters. ^ and $ match start/end of lines.
xExtended mode: Ignores whitespace (except when escaped or in a character class) and allows comments (# ...) within the regex. Useful for complex patterns.
oOnce: Perform #{...} interpolations only once, the first time the regex literal is evaluated. Less common in modern Ruby due to performance improvements.

Ruby’s default string encoding is UTF-8, and regexes handle Unicode characters correctly. Older modifiers like u,e,s,n for specific encodings are generally not needed with modern Ruby unless dealing with legacy data in specific encodings.

Most characters match themselves. Special metacharacters like . * + ? ^ $ ( ) [ ] { } | \ have specific meanings and must be escaped with a backslash (\) if you want to match them literally.

The following table lists common regex syntax elements:

PatternDescription
^Matches the beginning of a line (or string if not in multiline mode).
$Matches the end of a line (or string if not in multiline mode, before a newline).
.Matches any single character except a newline (unless in multiline mode with /m).
[…]Character class: Matches any single character within the brackets. E.g., [aeiou] matches any vowel.
[^…]Negated character class: Matches any single character NOT in the brackets.
re*Matches 0 or more occurrences of the preceding expression re (greedy).
re+Matches 1 or more occurrences of re (greedy).
re?Matches 0 or 1 occurrence of re (greedy).
re*?Matches 0 or more occurrences (non-greedy/lazy).
re+?Matches 1 or more occurrences (non-greedy/lazy).
re??Matches 0 or 1 occurrence (non-greedy/lazy).
re{n}Matches exactly n occurrences of re.
re{n,}Matches n or more occurrences of re.
re{n,m}Matches at least n and at most m occurrences of re.
a|bAlternation: Matches either a or b.
(re)Capturing group: Groups re and captures the matched text. Accessed via MatchData object or $1, $2, etc.
(?re)Named capturing group: Captures re and assigns it a name. Accessed via match_data[:name].
(?:re)Non-capturing group: Groups re but does not capture the match.
\wMatches a word character (alphanumeric plus underscore: [a-zA-Z0-9_])
\WMatches a non-word character.
\sMatches a whitespace character (space, tab, newline, etc.).
\SMatches a non-whitespace character.
\dMatches a digit ([0-9])
\DMatches a non-digit.
\AMatches the beginning of the string.
\ZMatches the end of the string, or before a newline at the end of the string.
\zMatches the absolute end of the string.
\bMatches a word boundary (position between a word character and a non-word character).
\BMatches a non-word boundary.
\1, \2, …Backreference: Matches the text captured by the nth capturing group.
\kBackreference: Matches the text captured by the named group ‘name’.

When matching entire strings, it’s often safer to use \A (start of string) and \z (end of string) instead of ^ and $ respectively. ^ and $ can match the start/end of lines within a multiline string if the /m modifier is used, which might not be the intended behavior for input validation.

Named captures make complex regexes more readable and maintainable.

#!/usr/bin/env ruby
log_line = "INFO: Request processed in 0.025s by UserController#index"
pattern = /^(?<level>\w+): (?<message>.*) in (?<duration>\d+\.\d+)s by (?<controller>\w+)#(?<action>\w+)$/
match = log_line.match(pattern)
if match
puts "Log Level: #{match[:level]}" # Access by name
puts "Message: #{match['message']}" # Access by name as string
puts "Duration: #{match[:duration]}"
puts "Controller: #{match[:controller]}"
puts "Action: #{match[:action]}"
else
puts "Log line did not match the pattern."
end

This will produce:

Log Level: INFO
Message: Request processed
Duration: 0.025
Controller: UserController
Action: index

The String class provides sub (substitute first occurrence) and gsub (substitute all occurrences) methods for search and replace operations using regular expressions. Their bang counterparts (sub! and gsub!) modify the string in place.

#!/usr/bin/env ruby
phone = "Call (123) 456-7890 or 987-654-3210 # Support lines"
# Remove comments
cleaned_phone = phone.sub(/\s*#.*$/, "")
puts "Cleaned Phone: #{cleaned_phone}" #=> Call (123) 456-7890 or 987-654-3210
# Standardize phone format (example, very basic)
# This gsub uses a block for replacement logic
standardized_phone = cleaned_phone.gsub(/\(?(\d{3})\)?[\s-]?(\d{3})[\s-]?(\d{4})/) do |match_text|
# $1, $2, $3 refer to captured groups from the current match
"+#{$1}-#{$2}-#{$3}"
end
puts "Standardized Phone: #{standardized_phone}" #=> Call +123-456-7890 or +987-654-3210
text = "ruby is fun, Ruby is powerful."
# Case-insensitive replacement of "ruby"
new_text = text.gsub(/ruby/i, "Crystal")
puts "New text: #{new_text}" #=> Crystal is fun, Crystal is powerful.

The replacement argument in sub and gsub can also be a hash, where keys are matched strings and values are their replacements, or it can use backreferences like \1, \2 or \k<name>.

Other useful String methods involving regexes include scan (returns an array of all matches), split (splits string by regex delimiter), and index (finds index of first match).