DOI系统对合理标识符的构成基本上没有任何有用的限制。但是,能够从 PDF、网页等中提取 DOI 对于引文信息等非常有用。
有没有一种可靠的方法来识别文本块中的 DOI 而无需假设“doi:”前缀?(任何可接受的语言,首选正则表达式,并且必须避免误报)
好的,我目前正在从自由格式文本 (XML) 中提取数千个 DOI,我意识到我以前的方法存在一些问题,即关于编码实体和尾随标点符号,所以我继续阅读规范,这是我最好的可以来。
DOI 前缀应由一个目录指示符和一个注册者代码组成。这两个组件应由句号(句号)分隔。
目录指示符应为“10”。目录指示符将整个字符串集(前缀和后缀)区分为解析系统中的数字对象标识符。
很简单,首字母会\b
阻止我们“匹配”不以 开头的“DOI” 10.
:
$pattern = '\b(10[.]';
DOI 前缀的第二个元素是注册人代码。注册人代码是分配给注册人的唯一字符串。
此外,所有分配的注册人代码都是数字,并且长度至少为 4 位,因此:
$pattern = '\b(10[.][0-9]{4,}';
如果需要,为了管理方便,注册人代码可以进一步划分为子元素。注册人代码的每个子元素之前都应有句号。
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*';
DOI 语法应由 DOI 前缀和 DOI 后缀组成,由正斜杠分隔。
但是,这不是绝对必要的,第 2.2.3 节指出不常见的后缀系统可以使用其他约定(例如10.1000.123456
代替10.1000/123456
),但让我们稍微松懈一下。
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/';
DOI 号不区分大小写,可以包含 Unicode 合法图形字符中的任何可打印字符。DOI 后缀应由注册人选择的任意长度的字符串组成。每个后缀对于它之前的前缀元素应该是唯一的。唯一的后缀可以是一个序列号,或者它可能包含从另一个系统生成或基于另一个系统的标识符。
现在这是变得更棘手的地方,从我处理的所有 DOI 中,我[0-9a-zA-Z]
在它们的后缀中看到了以下字符(当然,除了) :.-()/:-
-- 所以,虽然它不存在,但 DOI10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7
是完全合理的。
合乎逻辑的选择是使用\S
PCRE [[:graph:]]
POSIX 类,所以让我们这样做:
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/\S+'; // or
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/[[:graph:]]+';
现在我们有一个难题,[[:graph:]]
该类是该类的超集[[:punct:]]
,其中包括在自由文本或任何标记语言中容易找到的字符:"'&<>
等等。
现在让我们使用负前瞻过滤标记:
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+'; // or
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])[[:graph:]])+';
以上内容应涵盖编码实体 ( &
)、属性引号 ( ["']
) 和打开/关闭标签 ( [<>]
)。
与标记语言不同,自由文本通常不使用标点符号,除非它们以至少一个空格为界或放置在句子的末尾,例如:
这是一个很长的 DOI:
10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7
!!!
这里的解决方案是关闭我们的捕获组并声明另一个单词边界:
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+)\b'; // or
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])[[:graph:]])+)\b';
瞧,这是一个演示。
CrossRef 有一个建议,他们成功地测试了 99.3% 的 DOI(他们知道):
/^10.\d{4,9}/[-._;()/:A-Z0-9]+$/i
@Silas 健全性检查是个好主意。但是,正则表达式并未涵盖所有 DOI。第一个元素(当前)必须是 10,第二个元素(当前)必须是数字,但第三个元素几乎没有限制:
“合法字符是Unicode的合法图形字符。这里专门排除控制字符范围0x00-0x1F和0x80-0x9F...”
这就是真正的问题所在。在实践中,我从未见过使用空格,但规范特别允许使用它。基本上,似乎没有一种明智的方法来检测 DOI 的结束。
我确信此时它对 OP 没有太大帮助,但我想我会发布我正在尝试的内容,以防像我这样的其他人偶然发现这一点:
(10.(\d)+/(\S)+)
这匹配:“10 点数字斜线任何非空白”
但是对于我的使用(抓取 HTML),这是发现误报,所以我必须匹配上面的内容,加上去掉引号和大于/小于:
(10.(\d)+/([^(\s\>\"\<)])+)
我仍在测试这些,但到目前为止我感到充满希望。
这是我的尝试:
(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 的一部分。
尚未对此进行误报测试,但它似乎能够找到此页面中提到的所有边缘情况。
以下正则表达式应该可以完成这项工作(Perl 正则表达式语法):
/(10\.\d+\/\d+)/
您可以通过打开网址进行一些额外的健全性检查
http://hdl.handle.net/<doi>
和
http://dx.doi.org/<doi>
候选人doi在哪里,
并测试您 a) 获得 200 OK http 状态,b) 返回的页面不是服务的“DOI not found”页面。