一个只破译第二个字母的破译器

A decipher which only deciphers every second letter

所以我正在制作一个函数来破译 rot13 或凯撒密码的变体。但出于某种原因,它每隔一个字母就会破译一次……我不知道该改变或做什么。谢谢

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: FREE

这似乎工作正常

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'))

您有两个问题:

  • 在正则表达式上使用 g 标志时,正则表达式变为有状态的,并且 .test 方法会跟踪它在字符串中搜索的位置。 From MDN:

As with exec() (or in combination with it), test() called multiple times on the same global regular expression instance will advance past the previous match.

这意味着当你使用全局标志时,一旦正则表达式匹配了一个字符,它将不会匹配另一个直到它被重置:

const rgx = /\w/g;

console.log("Different results!:", rgx.test("a"), rgx.test("a"))

这就是您看到的“每个其他字符”替换的原因,因为循环中的 if 条件只会在每隔一次通过时执行。

要解决此问题,请避免在正则表达式上使用 g 标志,除非您需要有状态的行为。

第二期:

  • 当使用 .replace 方法并将字符串作为第一个参数时,只会替换该字符串的第一个实例。

这意味着如果您要替换的字符出现得较早,则将交换较早的字符,而不是当前索引处的字符。

console.log("Only the first is replaced!:", "aaaa".replace("a", "b"))

对此进行调整的一种方法是不使用 .replace 尝试交换字符,而是使用 .slice 重建字符串,同时更改其对应字符的编码字符。此方法确保更改的字符是您当前索引处的字符 i:

str = str.slice(0, i) + obj[str[i]] + str.slice(i+1)

综上所述,您可以像这样修改代码段:

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: FREE