在文档或页面中查找DOI

DOI系统基本上不构成对合理标识符构成有用的限制。 但是,将DOI引出PDF,网页等对于引用信息等是非常有用的。

有没有可靠的方法来识别一个文本块中的一个DOI,而不用假设'doi:'前缀? (任何可接受的语言,正则expression式都是首选,避免误报是必须的)

好吧,我目前从自由格式文本(XML)中提取数以千计的DOI,并且我意识到我以前的方法有一些问题,即关于编码实体和尾随标点符号,所以我继续阅读规范 ,这是最好的可以来。


DOI前缀应由一个目录指示符和一个注册人代码组成。 这两个组成部分应以句号(句点)分隔。

目录指示符应为“10”。 目录指示符将整组string(前缀和后缀)区分为分辨率系统内的数字对象标识符。

很简单,最初的\b防止我们“匹配”一个不以10.开头的“DOI” 10.

 $pattern = '\b(10[.]'; 

DOI前缀的第二个元素应该是注册人代码。 注册人代码是分配给注册人的唯一string。

此外,所有分配的注册人代码都是数字的,并且至less有4位数字,因此:

 $pattern = '\b(10[.][0-9]{4,}'; 

如果需要的话,注册人代码可以进一步分成子元素以便pipe理。 注册人代码的每个子元素之前都应有一个句号。

 $pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*'; 

DOI语法应由正斜杠分隔的DOI前缀和DOI后缀组成。

然而,这不是绝对必要的,第2.2.3节指出不常见的后缀系统可能会使用其他约定(如10.1000.123456而不是10.1000/123456 ),但是可以减less一些松弛。

 $pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/'; 

DOI号不区分大小写,可以包含Unicode的合法graphics字符中的任何可打印字符。 DOI后缀应由注册人select的任意长度的string组成。 每个后缀对于前面的前缀元素都是唯一的。 唯一的后缀可以是序列号,也可以包含从另一个系统生成或基于另一个系统生成的标识符。

现在,这是从我处理的所有DOI中变得更加棘手的地方,我看到以下字符(当然除了[0-9a-zA-Z]后缀.-()/:- -虽然它不存在,DOI 10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7是完全合理的。

合乎逻辑的select是使用\S[[:graph:]] PCRE POSIX类,所以让我们这样做:

 $pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/\S+'; // or $pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/[[:graph:]]+'; 

现在我们遇到了一个难题, [[:graph:]]类是[[:punct:]]类的超集,它包含了在自由文本或任何标记语言中容易find的字符: "'&<>等等。

现在让我们使用一个负面的lookahead来过滤标记:

 $pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+'; // or $pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])[[:graph:]])+'; 

上面应该包括编码的实体( & ),属性引号( ["'] )和打开/closures标签( [<>] )。

与标记语言不同,自由文本通常不会使用标点符号,除非它们至less有一个空格放在句尾,例如:

这是一个很长的DOI: 10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7 : 10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7 !!!

这里的解决scheme是closures我们的捕获组,并断言另一个字的边界:

 $pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+)\b'; // or $pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])[[:graph:]])+)\b'; 

, 这是一个演示 。

@西拉斯理智的检查是一个好主意。 然而,正则expression式并不涵盖所有的DOI。 第一个元素(当前)必须是10,而第二个元素(现在)必须是数字,但是第三个元素几乎不受限制:

“合法字符是Unicode的合法graphics字符,具体排除了控制字符范围0x00-0x1F和0x80-0x9F …”

这就是真正的问题所在。 在实践中,我从来没有见过空白,但规范特别允许它。 基本上,似乎没有一个明智的方式来检测DOI的结束

我相信这对OP来说并不是很有帮助,但是我想我会发布我正在尝试的,以防像我这样的其他人绊倒在这里:

 (10.(\d)+/(\S)+) 

这匹配:“10点数字斜杠什么,不是空白”

但是对于我的使用(刮HTML),这是发现误报,所以我必须匹配以上,加上摆脱引号和大于/小于:

 (10.(\d)+/([^(\s\>\"\<)])+) 

我仍然在testing这些,但是我现在感到很有希望。

这是我去的地方:

 (10[.][0-9]{4,}[^\s"/<>]*/[^\s"<>]+) 

还有一些有效的边缘情况下,这不会失败,但其他人似乎这样做:

  • 10.1007/978-3-642-28108-2_19
  • 10.1007.10/978-3-642-28108-2_19 (虚构例子,请参阅@ Ju9OR评论 )
  • 10.1016/S0735-1097(98)00347-7
  • 10.1579/0044-7447(2006)35\[89:RDUICP\]2.0.CO;2

此外,正确地丢弃一些虚假(X | HT)ML的东西,如:

  • <geo coords="10.4515260,51.1656910"></geo>

这是一个非常古老和回答的问题,但这是另一个潜在的替代品。

 \b10\.(\d+\.*)+[\/](([^\s\.])+\.*)+\b 

这假定白色空间不是DOI的一部分。

还没有testing过这个误报,但它似乎能够find本页提到的所有边缘案例。

下面的正则expression式应该做这个工作(Perl正则expression式语法):

 /(10\.\d+\/\d+)/ 

你可以通过打开url做一些额外的理智检查

 http://hdl.handle.net/<doi> 

 http://dx.doi.org/<doi> 

候选人在哪里,

并testing你a)得到一个200 OK的http状态,b)返回的页面不是服务的“DOI not found”页面。

Interesting Posts