ruby.onl / regex

Ruby Regex: Everything Perl Taught You, Plus Named Captures That Actually Work

2026-02-25

Ruby's regex engine is comparable to Perl's. Most patterns work identically. The main difference is that substitution uses methods (gsub, sub) instead of operators (s///). But Ruby has one trick that'll make Perl jealous: named captures that become real local variables. Yeah.

Part 1: Basic Matching

# Match operator (same as Perl) if line =~ %r~pattern~ puts "matched!" end # Negated match (same as Perl) if line !~ %r~pattern~ puts "no match" end # With flags line =~ %r~error~i # case-insensitive line =~ %r~^start~m # multiline (^ matches line starts) line =~ %r~complex~x # extended (ignore whitespace, allow comments)

Part 2: Substitution with gsub and sub

# Perl: $line =~ s/old/new/; # first occurrence $line =~ s/old/new/g; # all occurrences
# Ruby: line.sub(%r~old~, "new") # first occurrence - returns NEW string line.gsub(%r~old~, "new") # all occurrences - returns NEW string # To modify in place (like Perl's default): line.sub!(%r~old~, "new") # modifies line directly line.gsub!(%r~old~, "new") # modifies line directly
Without the !, the original string is NOT changed. This is the number one gotcha for Perl users. You will forget. You will stare at unchanged output for ten minutes. You will swear. Then you'll add the ! and move on.

Part 3: Extraction

# Extract first match line[%r~\d+~] # returns first number found, or nil # Extract capture group line[%r~(\d+)~, 1] # returns first capture group # Extract all matches line.scan(%r~\d+~) # returns array of ALL matches # Extract all capture groups line.scan(%r~(\w+)=(\w+)~) # returns array of [key, value] pairs

Part 4: Capture Groups

# Numbered captures ($1, $2, etc.) - same as Perl if line =~ %r~(\d{4})-(\d{2})-(\d{2})~ year = $1 month = $2 day = $3 end # Named captures become LOCAL VARIABLES (Ruby magic!) if line =~ %r~(?<ip>\d+\.\d+\.\d+\.\d+).*(?<status>\d{3})~ puts "#{ip} returned #{status}" # ip and status are real local variables now! end
Named captures becoming local variables is a Ruby feature Perl doesn't have. In Perl you'd access them as $+{ip}. In Ruby they just appear as variables. It's borderline sorcery.

Part 5: Regexp.union

Combine multiple patterns into one with automatic escaping:
# Combine regex patterns pattern = Regexp.union(%r~Ruby\d~, %r~test~i, "literal.text") # => /(Ruby\d|[tT][eE][sS][tT]|literal\.text)/ # Strings are auto-escaped! Regexp.union("a.b", "c*d") # => /a\.b|c\*d/ # From an array of keywords keywords = %w~error warning critical~ pattern = Regexp.union(keywords) # => /error|warning|critical/ # Use it lines.select { |l| l =~ pattern }
Perl equivalent would be manually joining with | and worrying about escaping: my $pattern = join('|', map { quotemeta } @keywords);. Ruby just does it.

Part 6: The match Method

# Returns MatchData object (more info than $~) m = "hello 123 world".match(%r~(\w+) (\d+)~) m[0] # => "hello 123" (full match) m[1] # => "hello" (first capture) m[2] # => "123" (second capture) m.pre_match # => "" (before match) m.post_match # => " world" (after match) # With named captures m = "2024-01-15".match(%r~(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})~) m[:year] # => "2024" m[:month] # => "01" m[:day] # => "15"

Part 7: The Flag Gotcha

Flag Meaning Same as Perl?
i Case-insensitive Yes
m Multiline (. matches newline) Like Perl's s flag!
x Extended (comments/whitespace) Yes
Ruby's m flag is Perl's s flag. In Perl, m makes ^/$ match line boundaries. In Ruby, ^/$ ALWAYS match line boundaries. This will confuse you exactly once.

Part 8: gsub with Block

# Transform matches with a block line.gsub(%r~\b\w+\b~) { |match| match.capitalize } # Perl equivalent: # $line =~ s/\b\w+\b/\u\L$&/ge;

Part 9: Practical One-Liners

# Replace all IPs with REDACTED ruby -pe '$_.gsub!(%r~\d+\.\d+\.\d+\.\d+~, "REDACTED")' access.log # Extract all email addresses ruby -ne '$_.scan(%r~[\w.+-]+@[\w.-]+~).each { |e| puts e }' file.txt # Swap first two CSV columns ruby -pe '$_.sub!(%r~^([^,]+),([^,]+)~, "\\2,\\1")' data.csv

Created By: Wildcard Wizard. Copyright 2026