Regular expressions are a way to perform expressive string matching in a lightweight way. In other words, if I’m ever looking inside a large string for a set of substrings that match some pattern, I can use regular expressions. Regular expressions are useful for things like:

  • Renaming strings of a certain pattern in a file

  • Validating form input in a web app

  • Extracting data from sets of documents (like error logs) and aggregating them into a human readable form

A few basic regular expressions

In Ruby, regular expressions have the form /regex/options where regex is the regular expression, and options specify a set of options. Usually the options are empty, but you can add things like i for case insensitivity, and x to ignore whitespace (and a few others you can read about).

For example, /hello/ matches exactly the string “hello”. You can regular expressions in two main ways:

1 if /Hello/ =~ "Hello, World!" then
2   puts "matched"
3 else 
4   puts "no match" 
5 end

Inside the true branch, a few variables are set so that you can get the match data. For example, the following regular expression captures strings of the form “Hello, <string>!”, where <string> contains one or more characters (that’s what the + after the \S) that aren’t spaces (that’s what the \S means):

1 if /Hello, (\S+)!/ =~ "Hello, Carol!" then
2   puts $~[1]
3 else 
4   puts "no match" 
5 end

Some other regular expressions:

  • /s(i|a)mple/ matches “simple” or “sample”, the | operator is an or.

  • /a*/ matches zero or more ‘a’s, including none. /ja*r/ matches “jr”, “jar”, “jaar”, etc…

  • /a+/ matches one or more ‘a’s, including none. /ja+r/ matches “jar”, “jaar”, etc…

  • . matches any single character: /d.g/ matches “dog”, “dag”, “d&g”, etc…

  • /a?/ matches zero or one ‘a’s.

  • [abc] matches any single character of “a”, “b”, or “c”

  • [a-z] matches any character a through z. So matching lowercase strings would be [a-z]+.

  • ^ matches the start of a line (and $ end of line), so /^hello/ matches “hello” at the beginnning of a line. This is useful when your regular expressions work over files.

  • Lots of cases for things like any digit \d, /\d+/ matches any string of consecutive digits.

MatchData

When you write regular expressions, you can capture things by putting them in parenthesis. For example, in the regex /Hello, (\S+)!/, the contiguous string of non-space characters before the exclamation point is captured. If you use =~, the global variable $~ is set to contain the resulting MatchData class. You should read the documentation for MatchData here to get the details. The basic idea is that MatchData is indexed like an array, so data[1] returns the first match. $~ is a global variable that is set in the case when the =~ operator matches a regular expression. I find this to be a bit mystical and don’t like using global variables. Instead, I’m more comfortable matching explicitly:

1 matches = /Hello, (\S+)!/.match("Hello, Carol!") # Set to `nil` if not matched
2 if matches then
3   puts matches[1]
4 else 
5   puts "no match" 
6 end

The MatchData documentation contains a lot of good information and examples.

Different syntax for regular expresisons

Each languge has subtly different sytnax for regular expressions. And some langauges offer extended features that are more powerful than “formal” regular expressions. For this part of the class, we’ll be using Ruby regular expressions. There are two helpful tools for this:

  • The official documentation is well written and basic enough.

  • The Rubular tool which interactively builds regular expressions and tests them in real time. I see a lot of students using this, and I’ve found it most helpful.

The basic basic regular expressions that are pretty common between languages are:

  • . matches a single character

  • E* matches any number of Es, where E is also a regex

  • E+ matches one or more Es

  • E? matches zero or one Es.

For example emacs has a very powerful regular expression tool that you can use, M-x regexp-builder