Regular expressions are an extremely powerful tool. As this quite informative page on Wikipedia puts it, a regular expression is
“a sequence of characters that defines a search pattern”
We can indeed use regular expressions to look for patterns, sequences of characters we are interested in, in strings or text files. If you are totally new to the concept, this can be difficult to grasp at the beginning. However we promise that once you get it, you will love it and a whole world of exciting possibilities opens up.
Uses of regular expressions
You can use them to “parse” text files in search, for example, of particular sequences or chunks of characters, and set those aside for further analysis. Imagine parsing a text file with thousands of FASTA sequences and taking out all the sequence IDs for some particular purpose.
Or you can perform a smart “search and replace” in a text file, way more powerful than what we saw already with explode(), as while explode() works with very precisely defined substrings or characters as delimiters for string splitting, a regular expression can look for a way more loosely defined pattern that will allow us to catch matches with substring those exact sequence of characters we do not know beforehand.
In the analysis of biological entities such as nucleotide or protein sequences, special linear (sequence-based) patterns are very often linked to interesting biological functional properties such as for example the ability to interact with proteins in order to form complexes or for other regulatory reasons. Just think about how gene promoters are rich in transcription factors binding sites, that can often be defined by loose linear patters, that determine if and how much a particular gene will be expressed in a certain cellular environment.
Regular characters and metacharacters in regular expressions
There are two kind of characters that can be used inside a regular expression: regular characters, that have a “literal” meaning, and metacharaters, that have a special meaning.
- Every character that is not a metacharacter is a regular character.
- Every regular character matches itself.
- In order to match a metacharacter used within a regular expression literally, we need to escape it with a backslash.
All of this will become more clear by looking at the examples shown below in this section and maybe just a little bit of practice.
Here is a summary of the most used metacharacters, thanks to Wikipedia:
The PHP preg_match() function, basic use
Learning to write regular expressions is better done within a framework that allows us to use them in practical examples. There are several predefined PHP functions that can take regular expressions as arguments and perform actions with them. The more “popular” and widely used is called preg_match(). In the most basic use, it allows us to check if a particular regular expression finds a match into a given string. The regular expression is passed as first argument, the string as second argument. It will return true if a match is found and false if a match is not found. Used in this way, it will not provide any information on exactly “what” was found into the string that matches the pattern defined in the regular expression, just if a match was found or not.
Let’s get into some practical examples to be better grasp what are regular expressions and how you can use them in the PHP programming language. We will slowly introduce a few metacharacters as we proceed.
The ^ and $ metacharacters
To better understand the following example:
- The general syntax for a regular expression is to write it as a string enclosed between forward slashes: “/sequence of characters and meta characters/”
- Regular expressions are case sensitive. You can make them case insensitive by adding an i after the final forward slash, see $regexp6 below
- The ^ character will match the beginning of the target string
- The $ character inside the expression will match the end of the target string
- Before looking at the script output included after the code, try to guess by yourself if the sample regular expressions will match or not the target string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
<?php $mystring = "Hello, world"; $regexp1 = "/world/"; // Matches the string "world" everywhere in the target $regexp2 = "/^world/"; // Matches the string "world" if at the beginning of the target string $regexp3 = "/Hello/"; // Matches the string "Hello" everywhere in the target $regexp4 = "/^hello/"; // Matches the string "hello" if at the beginning of the target string $regexp5 = "/^Hello/"; // Matches the string "Hello" if at the beginning of the target string // mind that case matters and "Hello" is not the same as "hello" $regexp6 = "/^hello/i"; // Matches the string "hello" if at the beginning of the target string // the added i makes the expression case-insensitive $regexp7 = "/world$/"; // Matches the string "world" if at the end of the target string $regexp8 = "/Hello$/"; // Matches the string "Hello" if at the end of the target string echo "<p>The target string is <strong>$mystring</strong></p>\n"; if(preg_match($regexp1,$mystring)){ echo "<p>Regular expression 1 matched</p>\n"; } else{ echo "<p>Regular expression 1 did not match</p>\n"; } if(preg_match($regexp2,$mystring)){ echo "<p>Regular expression 2 matched</p>"; } else{ echo "<p>Regular expression 2 did not match</p>\n"; } if(preg_match($regexp3,$mystring)){ echo "<p>Regular expression 3 matched</p>\n"; } else{ echo "<p>Regular expression 3 did not match</p>\n"; } if(preg_match($regexp4,$mystring)){ echo "<p>Regular expression 4 matched</p>\n"; } else{ echo "<p>Regular expression 4 did not match</p>\n"; } if(preg_match($regexp5,$mystring)){ echo "<p>Regular expression 5 matched</p>\n"; } else{ echo "<p>Regular expression 5 did not match</p>\n"; } if(preg_match($regexp6,$mystring)){ echo "<p>Regular expression 6 matched</p>\n"; } else{ echo "<p>Regular expression 6 did not match</p>\n"; } if(preg_match($regexp7,$mystring)){ echo "<p>Regular expression 7 matched</p>\n"; } else{ echo "<p>Regular expression 7 did not match</p>\n"; } if(preg_match($regexp8,$mystring)){ echo "<p>Regular expression 8 matched</p>\n"; } else{ echo "<p>Regular expression 8 did not match</p>\n"; } ?> |
Here’s the output of the script above:
The target string is Hello, world
Regular expression 1 matched
Regular expression 2 did not match
Regular expression 3 matched
Regular expression 4 did not match
Regular expression 5 matched
Regular expression 6 matched
Regular expression 7 matched
Regular expression 8 did not match
Checking if a short DNA sequence is present within a target sequence
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php $sequence = "GAATTC"; if(preg_match("/aat/", $sequence)){ // This will not match because lowercase echo "<p>1 - We have a match</p>\n"; } if(preg_match("/^AAT/", $sequence)){ // This will not match because AAT is not at the beginning of the sequence echo "<p>2 - We have a match</p>\n"; } if(preg_match("/AAT$/", $sequence)){ // This will not match because AAT is not at the end of the sequence echo "<p>3 - We have a match</p>\n"; } if(preg_match("/aat/i", $sequence)){ // This will match because i makes the expression case insensitive echo "<p>4 - We have a match</p>\n"; } if(preg_match("/AAT/", $sequence)){ // This will match because AAT is indeed in the sequence echo "<p>5 - We have a match</p>\n"; } if(preg_match("/^GA/", $sequence)){ // This will match because GA is at the beginning of the sequence echo "<p>6 - We have a match</p>\n"; } if(preg_match("/C$/", $sequence)){ // This will match because the sequence ends with a C echo "<p>7 - We have a match</p>\n"; } ?> |
This is the output of the script:
4 – We have a match
5 – We have a match
6 – We have a match
7 – We have a match
Extracting the FASTA header line from a FASTA sequence
In the following example we obtain a FASTA sequence from the UniProt web site with file_get_contents() as described previously. We use explode() to split a FASTA sequence into it’s composing individual lines, that will get stored into a $fasta_lines array. We then cycle through this lines array to identify the FASTA header line, that is the one that starts with a > sign, by using preg_match().
Remember that since > is not a metacharacter, it is a regular character that will simply match itself when used in a regular expression.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php $abl1_fasta = file_get_contents("http://www.uniprot.org/uniprot/P00519.fasta"); // See section 4-6 // We split the FASTA sequence into it's composing lines // by using the newline character \n as a delimiter in the explode() call $fasta_lines = explode("\n", $abl1_fasta); $fasta_header = ""; // We initialise the variable in which we will store the FASTA header line foreach($fasta_lines as $line){ if(preg_match("/^>/", $line)){ // We attempt to match a > at the beginning of the line $fasta_header = $line; break; // We have found the header line, we can stop the foreach cycle and move ahead } } if($fasta_header == ""){ // If nothing was found we still give a value to the $fasta_header variable $fasta_header = "header not found"; } echo "<p><strong>The FASTA header for the selected sequence is:</strong><br>\n$fasta_header</p>\n"; ?> |
Executing the code above generates the following output:
The FASTA header for the selected sequence is:
>sp|P00519|ABL1_HUMAN Tyrosine-protein kinase ABL1 OS=Homo sapiens GN=ABL1 PE=1 SV=4
The . and * metacharacters
The dot . metacharacter matches every character in a string except newlines. The asterisk metacharacter * indicates that the character that precedes it in the regular expression is repeated zero or more times.
The “/.*/” regular expression means “any character zero or more times”. It will match everything, even an empty string. To get a match to a full string, from start to end, you can use “/^.*$/”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php $string1 = "apples"; $string2 = "bananas"; $string3 = "GAATTC"; $string4 = ""; if(preg_match("/.*/", $string1)){ // It's a match echo "<p>\"/.*/\" matches $string1</p>\n"; } if(preg_match("/.*/", $string2)){ // It's a match echo "<p>\"/.*/\" matches $string2</p>\n"; } if(preg_match("/.*/", $string3)){ // It's a match echo "<p>\"/.*/\" matches $string3</p>\n"; } if(preg_match("/.*/i", $string4)){ // It's a match echo "<p>\"/.*/\" even matches an empty string!</p>\n"; } ?> |
Here’s the result:
“/.*/” matches apples
“/.*/” matches bananas
“/.*/” matches GAATTC
“/.*/” even matches an empty string!
The ? and + metacharacters
Similarly to the asterisk *, the ? and + metacharacters in a regular expression refer to the character that precedes them, adding a special meaning.
- * the character that precedes can be repeated zero or more times
- ? the character that precedes can be repeated zero or one time. A way to say that the character that precedes is optional, it can be present or not in the target for a match to occur
- + the character that precedes can be repeated one or more times
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php if(preg_match("/ATG?/", "AT")){ // It's a match as the G is optional echo "<p>\"/ATG?/\" matches AT</p>\n"; } if(preg_match("/ATG?/", "ATG")){ // It's a match echo "<p>\"/ATG?/\" matches ATG</p>\n"; } if(preg_match("/ATG?C/", "ATCTT")){ // It's a match as the G between AT and C is optional echo "<p>\"/ATG?C/\" matches ATCTT</p>\n"; } if(preg_match("/ATG+/", "AT")){ // No match, G must be present one or more times echo "<p>\"/ATG+/\" matches AT</p>\n"; } if(preg_match("/ATG+C/", "ATCTT")){ // No match, G must be present one or more times echo "<p>\"/ATG+C/\" matches ATCTT</p>\n"; } if(preg_match("/ATG+C/", "ATGGGGGCTT")){ // It's a match echo "<p>\"/ATG+C/\" matches ATGGGGGCTT</p>\n"; } ?> |
Here’s the output of the script above:
“/ATG?/” matches AT
“/ATG?/” matches ATG
“/ATG?C/” matches ATCTT
“/ATG+C/” matches ATGGGGGCTT
Defining character classes with square brackets []
By inserting a number of characters within square brackets in the context of a regular expression we indicate that each one of these characters could be a valid match for the position where the brackets are. We are defining a class of characters. For example [ATGC] means that either A or T of G or C are a valid match. Classes syntax supports intervals: [a-z] means every lowercase letter, [A-Z] means every uppercase letter, [a-zA-Z] means every letter, lowercase or uppercase, [a-zA-Z0-9] means every letter or number.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php if(preg_match("/TT[AGCT]CC/", "GGCTTACCTAT")){ // It's a match echo "<p style=\"font-family:courier;\">\"/TT[AGCT]CC/\" matches GGCTT<span style=\"color:red;\">A</span>CCTAT</p>\n"; } if(preg_match("/TT[AGCT]CC/", "CAGTTCCCTTA")){ // It's a match echo "<p style=\"font-family:courier;\">\"/TT[AGCT]CC/\" matches CAGTT<span style=\"color:red;\">C</span>CCTTA</p>\n"; } if(preg_match("/TT[AGCT]CC/", "TAGTTGCCCTT")){ // It's a match echo "<p style=\"font-family:courier;\">\"/TT[AGCT]CC/\" matches TAGTT<span style=\"color:red;\">G</span>CCCTT</p>\n"; } if(preg_match("/TT[AGCT]CC/", "TCTTTTCCGCG")){ // It's a match echo "<p style=\"font-family:courier;\">\"/TT[AGCT]CC/\" matches TCTTT<span style=\"color:red;\">T</span>TCCGCG</p>\n"; } ?> |
Running the code generates the following output:
“/TT[AGCT]CC/” matches GGCTTACCTAT
“/TT[AGCT]CC/” matches CAGTTCCCTTA
“/TT[AGCT]CC/” matches TAGTTGCCCTT
“/TT[AGCT]CC/” matches TCTTTTCCGCG
Consider the cutting pattern of restriction enzyme BanII:
5′ GRGCYC 3′
For the less initiated:
- Restriction enzymes are proteins that cut the DNA when a specific sequence pattern (the so-called “cutting site”) is present. Each enzyme has it’s own cutting site. Depending on the enzyme, the cutting site pattern may be very strict, with every nucleotide in every position univocally defined (such as “GGATCC” for the enzyme BamH1), or less strict, with some fixed positions where some precise nucleotide must be present and other positions where more than one nucleotide is allowed for the enzyme to cut (such as “GRGCYC” for the enzyme BanII)
- The “loosely defined” or, as they are called, “degenerated” or ambiguous positions can be specified by using the IUPAC alphabet. For example R means “either A or G” and Y means “either C or T”.
- Patterns with degenerated positions can match several different DNA sequences, the precise number depending on the number of degenerated positions and the number of different nucleotides allowed in each positions. For the pattern of BanII we have 2 degenerated positions and each can accommodate 2 different nucleotides, therefore the total number of possible combinations is 2 x 2 = 4, namely: GAGCCC, GAGCTC, GGGCTC, GGGCCC. Finding all of those within a target DNA sequence is a perfect job for regular expressions, that are indeed born for matching loosely defined patterns rather than strictly defined strings (that can of course still be easily matched).
Let us write a regular expression that will match all possible cutting sites for the BanII enzyme within a target DNA sequence. In order to use it properly to find all matches within the target DNA sequence we do need a more advanced use of the preg_match() function. Better, we would actually want to use the related function preg_match_all() in order to find all possible matches within the target sequence rather than just the first one, as preg_match() would do.
We will leave the matching to the next section of the book, for now let us just write the regular expression by using character classes:
1 2 3 4 5 6 7 |
<?php $banII_regexp ="/G[AG]GC[CT]C/"; ?> |
Hopefully you start to see the power of regular expressions and classes for pattern matching in the analysis of biological sequences here, and this is just a tiny simple example. More on this example in the next section.
With this information you may try to write yourself regular expressions for:
Bca77I: WCCGGW
Bco118I: RCCGGY
Character classes shortcuts
Here are a few handy shortcuts for some frequently used character classes
\d => matches any number, equivalent to [0-9]
\D => matches every character that is not a number, equivalent to [^0-9] (the ^ inside square brackets has a negation meaning)
\w => Every word character, equivalent to [A-Za-z0-9_]. Letters, numbers and the underscore.
\W => Every non word character, equivalent to [^A-Za-z0-9_] (examples of non word could be a newline \n, a tab \t or a comma ,)
\s => matches spaces
\l => Lowercase letters, equivalent to [a-z]
Quantifying characters with curly brackets {}
With *, ? and + you can specify if a character in the regular expression should be present in the target string zero or more times, zero or one time, one or more times. What if you wanted a character to be present 5 to 13 times? Exactly 25 times? 100 to 300 times? 5 or more times? 500 or more times? You can specify all these kind of options with curly brackets:
{5,} the character that precedes is repeated at least 5 times
{25} exactly 25 times
{100,300} from 100 to 300 times
{5,13} from 5 to 13 times
etc…
Let us make an example with some humor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php $you_said = array("AAAAAAAAAARGH!", "AAAAAAAAAAAAAAAAAAAARGH!", "AAAAARGH!", "ARGH!"); $desperation_regexp ="/A{3,50}RGH!$/"; // Come on, just one or two As // do not qualify the scream as a desperation one foreach($you_said as $scream){ if(preg_match($desperation_regexp,$scream)){ echo "<p>Looks like you are desperate, you said $scream</p>"; } else{ echo "<p>You are apparently OK, you just said $scream</p>"; } } ?> |
Please feel free to run the code above yourself, play with it and generate variations. What about checking for success with YAHOOOOOOOOOOOO…
We have covered the basics here. We know how to write simple regular expressions and to check if a pattern defined by a regular expression finds a match inside a string. However we still did not learn how to extract from the string what actually matched, which is an essential skill. In order to do that, we need to call the preg_match() function with a third argument. Also, we could add additional call flags to see where exactly the match is inside the string. Last but not least, we may want to retrieve all the matches of the regular expression in the string, not just the first one as preg_match() does, this can be accomplished with the preg_match_all() function. We will start to tackle these topics in the next section, stay tuned!
Regular expressions tutorial at regular-expressions.info
Regular expressions PHP engine at regular-expressions.info
Chapter Sections
[pagelist include=”435″]
[siblings]