Using regex for type validation

Viewed 66

I'm stating to learn regex and I'm trying to solve a problem using it. I need to write a function that gets a VCF file and need to check some columns in the files and see if they stand the requirements.

The first column should start with “chr” and end with any number between 1-99 or one of the letters “M,X,Y”. Second column need to be all int numbers that are bigger than 0. 4th and 5th columns need to be one of the next letters “ATCG” (only one of them). If one of the statements are wrong even in one row it should return false.

there's the code I wrote:

def isVCF(file):
    with open(file, "r+") as my_file:
        lines = my_file.readlines()
        for line in lines:
            columns = line.split("\t")
        num_format = re.compile("^[+]?[1-9][0-9]*\.?[0-9]+$")
        if (re.match(r"^chr(?:[1-9][0-9]?|[XYM])$", columns[0]) 
            and re.match(num_format, columns[1])
            and re.match(r"^[ATGC]$", columns[3]) 
            and re.match(r"^[ATGC]$", columns[4])): 
            return True
        else:
            return False

I checked two files - one should return True and the other False, But I get True on both of them, so I tried to do the test line by line and still got True. example of the file:

ChrX, 74226650, ., T, C, 50, ., DP=385;VDB=0;SGB=-0.693147;RPB=0.982669;MQB=1;BQB=0.947576;MQ0F=0;AC=2;AN=2;DP4=0,95,0,289;MQ=20, GT:PL:DP, 1/1:78,127,0:384

the output should be or true or false depends on the conditions.

Any help is appreciated!

1 Answers

You need to solve the problems one by one:

  • The first column should start with chr and end with any number between 1-99 or one of the letters M,X,Y - chr(?:0?[1-9]|[1-9][0-9]|[MXY])
  • Second column need to be all int numbers that are bigger than 0 - 0*[1-9][0-9]*
  • 4th and 5th columns need to be one of the next letters “ATCG” (only one of them) - [ATCG].

Now, considering the column delimiter is the one you used in the code sample (a TAB char, \t), you can use

def isVCF(file):
    num_format = re.compile(r"^chr(?:0?[1-9]|[1-9][0-9]|[MXY])\t0*[1-9][0-9]*\t[^\t]*(?:\t[ATCG]){2}\t", re.I)
    with open(file, "r+") as my_file:
        for line in my_file:
            if not num_format.match(line):
                return False
        return False

See the regex demo.

Note I used re.I to turn on case insensitivity because you use both chr and Chr in the question, if you need to only match Chr and chr this way, remove re.I and replace chr with (?i:chr).

Details:

  • ^ - start of string
  • chr (start of Column 1) - a chr string
  • (?:0?[1-9]|[1-9][0-9]|[MXY]) (end of Column 1): an optional 0 and then a non-zero digit, or a digit from 1 to 9 and then any one digit (0 - 99), or a letter from MXY set
  • \t - a tab
  • 0*[1-9][0-9]* (Column 2): zero or more 0s, a non-zero digit and then any zero or more digits
  • \t - a tab
  • [^\t]* (Column 3) - any zero or more chars other than a tab
  • (?:\t[ATCG]){2} (Column 4 and 5) - tab, a letter from ATCG set, two times
  • \t - a tab.
Related