regex - Encontrar un DOI en un documento o página
(6)
@Silas La comprobación de cordura es una buena idea. Sin embargo, la expresión regular no cubre todos los DOI. El primer elemento debe (actualmente) ser 10, y el segundo elemento debe (actualmente) ser numérico, pero el tercer elemento apenas está restringido:
"Los caracteres legales son los caracteres gráficos legales de Unicode. Esto excluye específicamente los rangos de caracteres de control 0x00-0x1F y 0x80-0x9F ..."
y ahí es donde reside el verdadero problema. En la práctica, nunca he visto espacio en blanco utilizado, pero la especificación lo permite específicamente. Básicamente, no parece haber una manera sensata de detectar el final de un DOI.
El sistema DOI básicamente no tiene limitaciones útiles sobre lo que constituye un identificador razonable . Sin embargo, ser capaz de extraer DOI de archivos PDF, páginas web, etc. es bastante útil para obtener información de citas, etc.
¿Existe una manera confiable de identificar un DOI en un bloque de texto sin asumir el prefijo ''doi:''? (cualquier lenguaje aceptable, expresiones preferidas y evitar falsos positivos es obligatorio)
Aquí está mi ir en eso:
(10[.][0-9]{4,}[^/s"/<>]*/[^/s"<>]+)
Y un par de casos límite válidos donde esto no falla, pero otros parecen hacer:
-
10.1007/978-3-642-28108-2_19
-
10.1007.10/978-3-642-28108-2_19
(ejemplo ficticio, vea el comentario de @J9OR ) -
10.1016/S0735-1097(98)00347-7
-
10.1579/0044-7447(2006)35/[89:RDUICP/]2.0.CO;2
Además, descarta correctamente algunas cosas falsas (X | HT) ML como:
-
<geo coords="10.4515260,51.1656910"></geo>
Esta es una pregunta muy antigua y respondida, pero aquí hay otro sustituto potencial.
/b10/.(/d+/.*)+[//](([^/s/.])+/.*)+/b
Esto supone que el espacio en blanco no es parte del DOI.
No he probado esto en busca de falsos positivos, pero parece ser capaz de encontrar todos los casos extremos mencionados en esta página.
Estoy seguro de que no es muy útil para OP en este momento, pero pensé que publicaría lo que estoy intentando en caso de que alguien más como yo se tropiece con esto:
(10.(/d)+/(/S)+)
Esto coincide: "Número de 10 puntos barra algo-no-espacio en blanco"
Pero para mi uso (raspado HTML), esto fue encontrar falsos positivos, así que tuve que coincidir con el anterior, además de deshacerme de las comillas y mayor que / menos de:
(10.(/d)+/([^(/s/>/"/<)])+)
Aún estoy probando esto, pero me siento esperanzado hasta el momento.
La siguiente expresión regular debería hacer el trabajo (sintaxis Perge regex):
/(10/./d+///d+)/
Puede hacer una comprobación de cordura adicional abriendo las URL
http://hdl.handle.net/<doi>
y
http://dx.doi.org/<doi>
donde esta el candidato doi,
y comprobar que a) obtiene un estado de 200 OK http, yb) la página devuelta no es la página "DOI no encontrado" para el servicio.
Ok, actualmente estoy extrayendo miles de DOI de texto de forma libre (XML) y me di cuenta de que mi enfoque anterior tenía algunos problemas, a saber, las entidades codificadas y la puntuación final, así que seguí leyendo las especificaciones y este es el mejor podría venir con.
El prefijo DOI se compondrá de un indicador de directorio seguido de un código de registrante. Estos dos componentes estarán separados por un punto (período) completo.
El indicador de directorio debe ser "10". El indicador de directorio distingue todo el conjunto de cadenas de caracteres (prefijo y sufijo) como identificadores de objetos digitales dentro del sistema de resolución.
Fácilmente, la inicial /b
nos impide "emparejar" un "DOI" que no comienza con 10.
.:
$pattern = ''/b(10[.]'';
El segundo elemento del prefijo DOI será el código de registrante. El código de registrante es una cadena única asignada a un registrante.
Además, todos los códigos de registrante asignados son numéricos y de al menos 4 dígitos de longitud, por lo que:
$pattern = ''/b(10[.][0-9]{4,}'';
El código del registrante puede dividirse en subelementos para su conveniencia administrativa si así lo desea. Cada subelemento del código de registro irá precedido de un punto.
$pattern = ''/b(10[.][0-9]{4,}(?:[.][0-9]+)*'';
La sintaxis de DOI estará formada por un prefijo DOI y un sufijo DOI separados por una barra inclinada.
Sin embargo, esto no es absolutamente necesario, la sección 2.2.3 establece que los sistemas de sufijo poco comunes pueden usar otras convenciones (como 10.1000.123456
lugar de 10.1000/123456
), pero permite reducir la holgura.
$pattern = ''/b(10[.][0-9]{4,}(?:[.][0-9]+)*/'';
El nombre DOI no distingue entre mayúsculas y minúsculas y puede incorporar cualquier carácter imprimible de los caracteres gráficos legales de Unicode. El sufijo DOI consistirá en una cadena de caracteres de cualquier longitud elegida por el registrante. Cada sufijo debe ser exclusivo del elemento de prefijo que lo precede. El sufijo único puede ser un número secuencial o puede incorporar un identificador generado a partir de otro sistema o basado en él.
Ahora aquí es donde se vuelve más complicado, de todos los DOI que he procesado, vi los siguientes caracteres (además de [0-9a-zA-Z]
por supuesto) en sus sufijos : .-()/:-
- entonces, mientras que no existe, el DOI 10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7
es completamente plausible.
La opción lógica sería usar /S
o la [[:graph:]]
clase PCRE POSIX, así que vamos a hacer eso:
$pattern = ''/b(10[.][0-9]{4,}(?:[.][0-9]+)*//S+''; // or
$pattern = ''/b(10[.][0-9]{4,}(?:[.][0-9]+)*/[[:graph:]]+'';
Ahora tenemos un problema difícil, la clase [[:graph:]]
es un superconjunto de la clase [[:punct:]]
, que incluye caracteres que se encuentran fácilmente en texto libre o en cualquier lenguaje de marcado: "''&<>
entre otros.
Permite simplemente filtrar los marcadores por ahora usando un lookahead negativo:
$pattern = ''/b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&/'<>])/S)+''; // or
$pattern = ''/b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&/'<>])[[:graph:]])+'';
Lo anterior debe cubrir entidades codificadas ( &
), comillas de atributos ( ["'']
) y etiquetas de apertura / cierre ( [<>]
).
A diferencia de los lenguajes de marcado, el texto libre generalmente no emplea caracteres de puntuación a menos que estén delimitados por al menos un espacio o colocados al final de una oración, por ejemplo:
Este es un largo DOI:
10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7
!!!
La solución aquí es cerrar nuestro grupo de captura y establecer otro límite de palabras:
$pattern = ''/b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&/'<>])/S)+)/b''; // or
$pattern = ''/b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&/'<>])[[:graph:]])+)/b'';
Y voilá , aquí hay una demostración .