Ответ 1
Хорошо, я в настоящее время извлекаю тысячи DOI из текста свободной формы (XML), и я понял, что мой предыдущий подход имел несколько проблем, а именно в отношении закодированных объектов и trailing пунктуация, поэтому я продолжал читать спецификацию, и это лучшее, что я мог бы придумать.
Префикс DOI должен состоять из указателя каталога, за которым следует код регистратора. Эти два компонента должны быть разделены полным stop (период).
Индикатор каталога должен быть "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 нечувствительно к регистру и может включать любые печатные символы из графических символов Юникода. DOI суффикс должен состоять из строки символов любой длины, выбранной регистранте. Каждый суффикс должен быть уникальным для префиксного элемента, который предшествует этому. Уникальный суффикс может быть порядковым номером, или он может включить идентификатор, сгенерированный из или на другой системе.
Теперь, когда он становится более сложным, из всех обработанных документов DOI я видел следующие символы (помимо [0-9a-zA-Z]
, конечно) в своих суффиксах: .-()/:-
- так, в то время как он не существует, DOI 10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7
вполне правдоподобен.
Логическим выбором было бы использовать класс \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:]]
, который включает символы, легко доступные в свободном тексте или любом языке разметки: "'&<>
среди других.
Позволяет просто фильтровать разметку на данный момент, используя отрицательный просмотр:
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+'; // or
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])[[:graph:]])+';
Вышеупомянутое должно охватывать закодированные объекты (&
), кавычки атрибутов (["']
) и теги open/close ([<>]
).
В отличие от языков разметки свободный текст обычно не использует знаки пунктуации, если они не ограничены хотя бы одним пространством или, помещенным в конце предложения, например:
Это длинный 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';
И voilá, вот демонстрация.