7
$\begingroup$

When I look for a regular expression for InChI, I succeed on EBI's MIRIAM Registery website:

^InChI\=1S?\/[A-Za-z0-9]+(\+[0-9]+)?(\/[cnpqbtmsih][A-Za-z0-9\-\+\(\)\,\/]+)*$

And indeed it works for some examples (using Python's re module):

import re

pattern = '^InChI\=1S?\/[A-Za-z0-9]+(\+[0-9]+)?(\/[cnpqbtmsih][A-Za-z0-9\-\+\(\)\,\/]+)*$'

s = 'InChI=1S/C2H6O/c1-2-3/h3H,2H2,1H3'

re.findall(pattern, s)  

will print

[('', '/c1-2-3/h3H,2H2,1H3')]

However, if I take other examples, as e.g. this one

s2 = 'InChI=1S/C6H12O6/c7-1-2-3(8)4(9)5(10)6(11)12-2/h2-11H,1H2/t2-,3-,4+,5+,6?/m1/s1'

it fails

re.findall(pattern, s2)

will return

[]

What is the correct and general regular expression for InChI?

EDIT

If I see this correctly, in the pattern from above, question marks are not taken into account which are used to mark both “unknown” and “undefined” chiral centres. If one adjusts the regex accordingly, it at least works for the example above; whether it then holds for all InChI is unclear, of course.

pattern = '^InChI\=1S?\/[A-Za-z0-9]+(\+[0-9]+)?(\/[cnpqbtmsih][A-Za-z0-9\-\+\(\)\,\/\?]+)*$'

s2 = 'InChI=1S/C6H12O6/c7-1-2-3(8)4(9)5(10)6(11)12-2/h2-11H,1H2/t2-,3-,4+,5+,6?/m1/s1'

re.findall(pattern, s2)

will return

[('', '/c7-1-2-3(8)4(9)5(10)6(11)12-2/h2-11H,1H2/t2-,3-,4+,5+,6?/m1/s1')]
$\endgroup$
10
  • 1
    $\begingroup$ Given the complexity of InChI, e.g. canonicalization, I doubt that there exists a regular expression that matches exactly the valid InChI strings. $\endgroup$
    – aventurin
    Commented Sep 3, 2017 at 22:18
  • $\begingroup$ @aventurin: What about this regular expression from the link above? There is also a github repository for this. I hardly know anything about InChI, but it feels that it might be possible to find such a regular expression but that the ones published do not cover all cases?! $\endgroup$
    – Cleb
    Commented Sep 3, 2017 at 22:32
  • $\begingroup$ If the InChI grammar would be on the same level of the Chomsky hierarchy as regular expressions (type-3) it would be possible to find such a regular expression. But as I've already said, I doubt this is the case. See the following mailing list post to get an idea about the complexity of the InChI grammar. sourceforge.net/p/inchi/mailman/message/1619718 $\endgroup$
    – aventurin
    Commented Sep 3, 2017 at 23:17
  • 2
    $\begingroup$ Your regular expression matches strings that are not valid InChIs, e.g. InChI=1S/2z or InChI=1S/CH100. If that's OK for your application, you should probably clarify your requirements. You could restrict to valid atomic symbols in the main layer by using something like ^InChI\\=1S?\\/((H|He|Li|Be|...)([1-9][0-9]*)?)+.... $\endgroup$
    – aventurin
    Commented Sep 4, 2017 at 20:56
  • 1
    $\begingroup$ I used the matches method with Java regular expressions. You might get different results with other regex implementations. $\endgroup$
    – aventurin
    Commented Sep 4, 2017 at 21:13

2 Answers 2

2
$\begingroup$

As long as you are not looking for something very specific, and you are not performing a sanity check of what you are matching against, the provided regular expression will match more often wrong than falsely not matching. It will even match more nonsense InChI than total number of existing ones. It is therefore not much better than the trivial catch them all:

^InChI\=1S?\/[^\s]+(\s|$)

The space (and some few others) is a character that will not be part of a correct InChI. The above will therefore match all correct InChI strings (and many more), and certainly more correct ones than the one given in the question (or in your answer).

The principal problem with the whole approach is basically given in the technical FAQ in section 5. Depending on what we are looking at, not all the layers will be present. There is not even a guarantee that a sum formula is present, as represented by the proton: InChI=1S/p+1. This one (and its relatives) is basically responsible why regex is doomed from the start, and that there is no correct regex in the first place.

If we wanted to implement only a tiny fraction of sanity control to reduce the number of mismatches, we will soon see that the whole string becomes about a page long. Let's examine the given regex a little more to actually understand what it and why it's attempt is futile.

^InChI\=1S?\/[A-Za-z0-9\.]+(\+[0-9]+)?(\/[cnpqbtmsih][A-Za-z0-9\-\+\(\)\,\/\?\;\.]+)*$

Beginning with

^  

matches the start of the string, which is not really necessary

 InChI\=1S?

matches the prefix, while leaving it open if standard or not

           \/[A-Za-z0-9\.]+(\+[0-9]+)?

matches a sum formula (even the silly ones) or anything that doesn't have a main layer and goes straight to isotopes or charges, etc.. Better would here be to make the sum formula optional, i.e. (\/[A-Za-z0-9\.]+)?

                                      (\/[cnpqbtmsih][A-Za-z0-9\-\+\(\)\,\/\?\;\.]+)*$

this is pretty much a fancy way of saying match any kind of layer, as often as you want,( or not,) in what order is just convenient, with whatever content. However, it does not match *, which it should: InChI=1S/2C2H5.Zn/c2*1-2;/h2*1H2,2H3; as given in the FAQ.

A much better approach would be to match the layers separately, to have a little control, because they have a predefined order. For example start with the connectivity 1.1 layer (\/c[0-9\-\*\;\(\)]+)?, move to the connectivity 1.2 or hydrogen layer (\/h[H0-9,\(\)\;]+)?. I am not too sure whether I caught all special characters that might be inserted. Then match on with charge (\/q(\+|\-)+[1-9]+[0-9]*)? and the proton balance \/p, the various stereochemistry layers, fixed layers, etc. There are plenty. I probably got the charge already halfway wrong, as I am not sure whether it needs ; or not.

Coming up with a correct and not too greedy regex for each layer is already a nice task, and if you can do something useful with it, good. For everything else, there is a reason that a software exists to create and parse those strings. For every other purpose the greedy match-it-all from the start should work perfectly fine. Now, InChIKeys are something very different ...

$\endgroup$
1
$\begingroup$

This might not be the final version but covers all cases I have encountered so far (if you find an InChI which cannot be matched with this expression, please post it in the comments along with a source, so that one can adapt the regex):

pattern = '^InChI\=1S?\/[A-Za-z0-9\.]+(\+[0-9]+)?(\/[cnpqbtmsih][A-Za-z0-9\-\+\(\)\,\/\?\;\.]+)*$'

The following changes were made compared to the one posted in the question:

  1. One also accounts for . in the chemical formula; check inchi-trust for an example
  2. One takes ? into account; check inchi-trust for a detailed explanation
  3. One also checks for ; ; check inchi-trust for an example
  4. One also has to take . into account later on, not only in the chemical formula. That is explained in "The IUPIC International Chemical identifier":

    /m parity inverted to obtain relative stereo ( 1 = inverted, 0 = not inverted, . = inversion does not affect the parity)

I guess this post will be updated in the future but for my examples it works now and all changes can be backed up by trustworthy sources.

The regular expression is updated now to the one I proposed. Apparently, they are happy to change their expression, so if you find cases where a valid InChI cannot be mapped, please report it to them or add it here in the comments. Clearly, this regular expression might also match strings which are actually not chemicals but at least one should be able to identify each valid InChI as InChI.

$\endgroup$
5
  • $\begingroup$ @downvoter: Could you please explain why you downvoted? It clearly addresses the question and provides explanation + sources. If there is something wrong, it would be nice to know what so that it can be fixed. $\endgroup$
    – Cleb
    Commented Sep 4, 2017 at 12:09
  • 3
    $\begingroup$ Why all the fuzz? If you are not performing a complete sanity check of every layer, then why match it at all? For example complete garbage like InChI=1/A100/cAfoobar would be matched. therefore InChI\=1S?\/[A-Za-z0-9\.\+]+(\/.+) already matches all correct InChI (and extracts the relevant layers, but it also matches all incorrect garbage. Somehow I fail to see how this string could be helpful anywhere... $\endgroup$ Commented Sep 6, 2017 at 14:55
  • $\begingroup$ As for your answer (I am not a down-voter), this is not a programming subsite, so you need to explain (either in the question or in the answer) what the parts of the regex actually do, and why they do it, right now this is all rather confusing. (Also please do not use any edit statements, revise the complete answer as necessary and make it read consistently. If anyone needs to know what changed, there is an extensive edit history available.) $\endgroup$ Commented Sep 6, 2017 at 14:59
  • $\begingroup$ @Martin-マーチン: All I need is a regex that tells me whether a given string has the correct form of an InChI, so it should identify all InChIs as correct. Clearly, one will then also match a lot of things that are not chemically relevant as your example but it should at least work for each valid InChI which the former regex apparently did not do. $\endgroup$
    – Cleb
    Commented Sep 6, 2017 at 15:33
  • $\begingroup$ @Martin-マーチン: And of course, if there is a better way of doing it, I would be more than happy to know about it. I just took the one provided, noticed that it failed and modified it that it works. If there is a better way that also checks each layer it would be great to know about it! $\endgroup$
    – Cleb
    Commented Sep 6, 2017 at 15:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.