Sé que hay formas más fáciles de obtener extensiones de archivo con JavaScript , pero en parte para practicar mis habilidades de expresiones regulares, quería probar y usar una expresión regular para dividir un nombre de archivo en dos cadenas, antes y después del punto final (carácter . ).
Esto es lo que tengo hasta ahora
const myRegex = /^((?:[^.]+(?:\.)*)+?)(\w+)?$/ const [filename1, extension1] = 'foo.baz.bing.bong'.match(myRegex); // filename1 = 'foo.baz.bing.' // extension1 = 'bong' const [filename, extension] = 'one.two'.match(myRegex); // filename2 = 'one.' // extension2 = 'two' const [filename, extension] = 'noextension'.match(myRegex); // filename2 = 'noextension' // extension2 = '' Intenté usar la anticipación negativa para decir 'solo coincide con un literal'. si es seguido por una palabra que termina en, así, cambiando (?:\.)* a (?:\.(?=\w+.))* :
/^((?:[^.]+(?:\.(?=(\w+\.))))*)(\w+)$/gm
Pero quiero excluir ese período final usando solo la expresión regular, y preferiblemente hacer que coincida 'sin extensión' en el grupo inicial, ¿cómo puedo hacer eso solo con la expresión regular?
Aquí está mi archivo borrador de expresiones regulares: https://regex101.com/r/RTPRNU/1
Para el primer grupo de captura, puede comenzar la coincidencia con 1 o más caracteres de palabras. Luego, opcionalmente, repite un . y de nuevo 1 o más caracteres de palabra.
Luego, puede usar un grupo opcional que no sea de captura que coincida con un archivo . y capturando 1 o más caracteres de palabras en el grupo 2.
Como el segundo grupo de no captura es opcional, la primera repetición debe ser voraz.
^(\w+(?:\.\w+)*?)(?:\.(\w+))?$El patrón coincide
^ Comienzo de cadena( Grupo de captura 1\w+(?:\.\w+)*? Haga coincidir 1+ caracteres de palabra y, opcionalmente, repita . y 1+ caracteres de palabra) Cerrar grupo 1(?: Grupo de no captura para emparejar como un todo\.(\w+) Coincide con un . y captura 1+ caracteres de palabra en el grupo de captura 2)? Cierre el grupo de no captura y hágalo opcional$ Fin de la cadenaDemostración de expresiones regulares
const regex = /^(\w+(?:\.\w+)*?)(?:\.(\w+))?$/; [ "foo.baz.bing.bong", "one.two", "noextension" ].forEach(s => { const m = s.match(regex); if (m) { console.log(m[1]); console.log(m[2]); console.log("----"); } });Otra opción como @ Wiktor Stribiżew publicó en los comentarios, es usar un punto no codicioso para hacer coincidir cualquier carácter para el nombre del archivo:
^(.*?)(?:\.(\w+))?$Si realmente desea usar expresiones regulares, sugeriría usar dos expresiones regulares:
// example with 'foo.baz.bing.bong' const firstString = /^.+(?=\.\w+)./g // match 'foo.baz.bing.' const secondString = /\w+$/g // match 'bong'¿Qué tal algo más explícito y preciso sin mirar alrededor?
variante de grupos con nombre ... /^(?<noextension>\w+)$|(?<filename>\w+(?:\.\w+)*)\.(?<extension>\w+)$/ (?<sin extensión>\w+)$|(?<nombre de archivo>\w+(?:\.\w+)*)\.(?<extensión>\w+)$/
sin grupos con nombre ... /^(\w+)$|(\w+(?:\.\w+)*)\.(\w+)$/
Las dos variantes que se acaban de mostrar se pueden acortar a 2 grupos de captura en lugar de los 3 grupos de captura de la variante anterior, lo que en mi opinión hace que sea más fácil trabajar con la expresión regular a costa de ser menos legible...
variante de grupos con nombre ... /(?<nombre de archivo>\w+(?:\.\w+)*?)(?:\.(? /(?<filename>\w+(?:\.\w+)*?)(?:\.(?<extension>\w+))?$/
sin grupos con nombre ... /(\w+(?:\.\w+)*?)(?:\.(\w+))?$/
const testData = [ 'foo.baz.bing.bong', 'one.two', 'noextension', ]; // https://regex101.com/r/RTPRNU/5 const regXTwoNamedFileNameCaptures = /(?<filename>\w+(?:\.\w+)*?)(?:\.(?<extension>\w+))?$/; // https://regex101.com/r/RTPRNU/4 const regXTwoFileNameCaptures = /(\w+(?:\.\w+)*?)(?:\.(\w+))?$/; // https://regex101.com/r/RTPRNU/3 const regXThreeNamedFileNameCaptures = /^(?<noextension>\w+)$|(?<filename>\w+(?:\.\w+)*)\.(?<extension>\w+)$/ // https://regex101.com/r/RTPRNU/3 const regXThreeFileNameCaptures = /^(\w+)$|(\w+(?:\.\w+)*)\.(\w+)$/ console.log( 'based on 2 named file name captures ...\n', testData, ' =>', testData.map(str => regXTwoNamedFileNameCaptures.exec(str)?.groups ?? {} ) ); console.log( 'based on 2 unnamed file name captures ...\n', testData, ' =>', testData.map(str => { const [ match, filename, extension, ] = str.match(regXTwoFileNameCaptures) ?? []; //] = regXTwoFileNameCaptures.exec(str) ?? []; return { filename, extension, } }) ); console.log( 'based on 3 named file name captures ...\n', testData, ' =>', testData.map(str => { const { filename = '', extension = '', noextension = '', } = regXThreeNamedFileNameCaptures.exec(str)?.groups ?? {}; return { filename: filename || noextension, extension, } }) ); console.log( 'based on 3 unnamed file name captures ...\n', testData, ' =>', testData.map(str => { const [ match, noextension = '', filename = '', extension = '', ] = str.match(regXThreeFileNameCaptures) ?? []; //] = regXThreeFileNameCaptures.exec(str) ?? []; return { filename: filename || noextension, extension, } }) ); .as-console-wrapper { min-height: 100%!important; top: 0; }