Así que estoy creando una función para descifrar rot13 o una variante del cifrado de César. Pero por alguna razón solo descifra cada segunda letra... No tengo ni idea de qué cambiar o hacer. Gracias
function rot13(str) { let regexp = /\w/gi; let obj = { "A": "N", "B": "O", "C": "P", "D": "Q", "E": "R", "F": "S", "G": "T", "H": "U", "I": "V", "J": "W", "K": "X", "L": "Y", "M": "Z", "N": "A", "O": "B", "P": "C", "Q": "D", "R": "E", "S": "F", "T": "G", "U": "H", "V": "I", "W": "J", "X": "K", "Y": "L", "Y": "M", }; for (let i = 0; i < str.length; i++) { if (regexp.test(str[i])) { str = str.replace(str[i], obj[str[i]]); } }; return str; } console.log(rot13("SERR")); //output: FEER //wanted output: FREETienes dos problemas:
g en una expresión regular, la expresión regular se vuelve con estado y el método .test realiza un seguimiento de en qué parte de la cadena está buscando. Desde MDN :Al igual que con
exec()(o en combinación con él),test()llamado varias veces en la misma instancia de expresión regular global avanzará más allá de la coincidencia anterior.
Esto significa que cuando usa la bandera global, una vez que la expresión regular haya coincidido con un carácter, no coincidirá con otro hasta que se haya reiniciado:
const rgx = /\w/g; console.log("Different results!:", rgx.test("a"), rgx.test("a"))Esto es lo que es responsable del reemplazo de "cada otro carácter" que ve, ya que la condición if en su ciclo solo se ejecutará en cada otro paso.
Para solucionarlo, simplemente evite usar el indicador g en la expresión regular a menos que necesite un comportamiento con estado.
Segundo problema:
.replace con una cadena como primer argumento, solo se reemplazará la primera instancia de esa cadena.Esto significa que si el carácter que está reemplazando aparece antes, será ese carácter anterior el que se intercambie, en lugar del que está en su índice actual.
console.log("Only the first is replaced!:", "aaaa".replace("a", "b")) Una forma de ajustar eso es en lugar de usar .replace para intentar intercambiar el carácter, reconstruir la cadena usando .slice mientras cambia el carácter codificado por su contraparte. Este método asegura que el carácter que se cambia es el de su índice actual i :
str = str.slice(0, i) + obj[str[i]] + str.slice(i+1)Poniendo todo eso junto, podrías modificar tu fragmento así:
function rot13(str) { let regexp = /\w/i; // No global match! let obj = { "A": "N", "B": "O", "C": "P", "D": "Q", "E": "R", "F": "S", "G": "T", "H": "U", "I": "V", "J": "W", "K": "X", "L": "Y", "M": "Z", "N": "A", "O": "B", "P": "C", "Q": "D", "R": "E", "S": "F", "T": "G", "U": "H", "V": "I", "W": "J", "X": "K", "Y": "L", "Z": "M", }; for (let i = 0; i < str.length; i++) { if (regexp.test(str[i])) { str = str.slice(0, i) + obj[str[i]] + str.slice(i+1) } }; return str; } console.log(rot13("SERR")); //output: FREEEsto parece que funciona bien
const rot13 = (str) => { const base = 'A'.charCodeAt() return str .split('') .map(c => String.fromCharCode(((c.charCodeAt() - base + 13) % 26) + base)).join('') } console.log(rot13('SERR'))