这篇文章将要练习的是回转13位密码(ROT13)。维基百科是这样描述ROT13的:
套用ROT13到一段文字上仅仅只需要检查字符字母顺序并替换它在13位之后的对应字母,有需要超过时则重新绕回26英文字母开头即可。 A换成N、B换成O、依此类推到M换成Z,然后序列反转:N换成A、O换成B、最后Z换成M。只有这些出现在英文字母里头的字符受影响;数字、符号、空白字符以及所有其他字符都不变。因为只有在英文字母表里头只有26个,并且26 = 2 × 13
,ROT13函数是它自己的逆反。如下图所示:
比如将DOG向后移13位:
D -> E,F,G,H,I,J,K,L,M,N,O,P,Q
O -> P,Q,R,S,T,U,V,W,X,Y,Z,A,B
G -> H,I,J,K,L,M,N,O,P,Q,R,S,T
即DOG转换为QBT。对照上面的图,也可以查出对应的转换值。
ASCII字符
在JavaScript中可以通过String.prototype.charCodeAt()
方法返回0至65535这间的整数,代表索引处理字符的UTF-16编码单元。可以通过这个方法查出每个字符对应的ASCII编码:
'a'.charCodeAt(); //97
'A'.charCodeAt(); //65
'abc'.charCodeAt(0); //97
'ABC'.charCodeAt(0); //65
传给String.prototype.charCodeAt()
的值如果是:
- 一个大于等于
0
,小于字符串长度的整数,如果不是一个数值,则默认为0
- 如果值小于
0
或大于字符串的长度,则返回NaN
那么,对应的ROT13中的字符对应的编码如下:
-----------------------------------------------------------------
| 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
-----------------------------------------------------------------
| A | B | C | D | E | F | G | H | I | J | K | L | M |
-----------------------------------------------------------------
| N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
-----------------------------------------------------------------
| 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
-----------------------------------------------------------------
如此一来,A-Z对应的就是65-90。而ROT13是将大写字符串将向后移13位,然后转换成对应的字符。那么:
- 小于65和大于90对应的就是小写字符a-z
- 大于等于65和小于等于77对应的就是大写字符A-M
- 大于等于78和小于等于90对应的就是大写字符N-Z
在实现ROT13功能,在整个函数中还需要使用到String.prototype.charAt()
方法和String.fromCharCode()
方法:
String.prototype.charAt()
: 返回字符串中指定位置的字符。字符串中的字符从左向右索引,第一个字符的索引值为0
,最后一个字符(假设该字符位于字符串 stringName 中)的索引值为stringName.length - 1
。 如果指定的index
值超出了该范围,则返回一个空字符串。String.fromCharCode()
: 根据指定的 Unicode 编码中的序号值来返回一个字符串。该方法返回一个字符串,而不是一个String
对象。
ROT13实现思路
创建一个rot13()
函数,并且给其传递一个str
字符串(也就是需要实现字符串向后移13位的字符)。实现整个功能,将按下面的几个步骤来:
- 声明一个变量
newString
,用来存一个空的字符串 - 使用
String.charCodeAt()
查询对应字符的ASCII序号 - 如果字符是非大写英文字母(序号小于
65
或大于91
),将该字符直接放到newString
中,并使用continue
进入下一个循环 - 如果序号小于78(A-M字母),使用
String.fromCharCode()
转换成该序号加13
的字符,并且放入newString
- 如果序号大于78(N-Z字母),使用
String.fromCharCode()
转找成该序号减13
的字符,并且放入newString
- 返回
newString
测试用例
为了检测rot13(str)
函数功能是否正常,可以通过下面的示例来进行检测:
rot13("SERR PBQR PNZC");
返回FREE CODE CAMP
rot13("SERR CVMMN!");
返回FREE PIZZA!
rot13("SERR YBIR?");
返回FREE LOVE?
rot13("GUR DHVPX OEBJA QBT WHZCRQ BIRE GUR YNML SBK.");
返回THE QUICK BROWN DOG JUMPED OVER THE LAZY FOX.
实现方案
下面罗列了一些实现rot13(str)
方案。虽然各种方案细节有所不同,但整个实现思路是按照上述介绍的思路进行的。
方法1
function rot13(str) {
var newString = [];
// charCodeAt()返回0-65535之间的整数
for (var i = 0; i < str.length; i++) {
var temp = str.charCodeAt(i);
// 如果字符是非大写英文字母(序号小于65或大于91),将该字符直接放到newString中,并使用continue进入下一个循环
if (temp < 65 || temp > 91) {
// 返回字符串指定位置的字符
newString.push(str.charAt(i));
continue;
// 如果序号大于77(N-Z字母),使用String.fromCharCode()转找成该序号减13的字符,并且放入newString
} else if (temp > 77) {
// String.fromCharCode()根据序号(指定的Unicode编码中的序号)值来返回一个字符串
newString.push(String.fromCharCode(temp - 13));
// 如果序号小于78(N-Z字母),使用String.fromCharCode()转找成该序号减13的字符,并且放入newString
} else {
newString.push(String.fromCharCode(temp + 13));
}
}
return newString.join("");
}
方法2
function rot13(str) {
var newString = "";
for (var i in str) {
var temp = str.charCodeAt(i);
if (temp < 65 || temp > 91) {
newString += str[i];
continue;
}
if (temp > 77) {
newString += String.fromCharCode(temp - 13);
}
else {
newString += String.fromCharCode(temp + 13);
}
}
return newString;
}
方法3
function rot13(str) {
var tempArr = str.split("");
var newString = [];
for (var i = 0; i < tempArr.length; i++) {
var temp = tempArr[i].charCodeAt(0);
var unicode = temp - 13;
if (unicode < 65) {
unicode += 26;
}
if (temp < 65 || temp > 90) {
newString.push(tempArr[i]);
} else {
newString.push(String.fromCharCode(unicode));
}
}
return newString.join("");
}
方法4
function rot13(str) {
var input = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
var output = ["n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"];
str = str.toLowerCase().split("");
for (var i = 0, l = str.length; i < l; i++) {
if (input.indexOf(str[i]) !== -1) {
var index = input.indexOf(str[i]);
str[i] = output[index];
}
}
str = str.join("").toUpperCase();
return str;
}
方法5
function rot13(str) {
var tempArr = str.split("");
var newString = [];
var alphaBets = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split("");
for (i = 0; i < tempArr.length; i++) {
var temp = tempArr[i];
var minified = alphaBets.indexOf(temp) !== -1 ? (alphaBets.indexOf(temp) < 13 ? newString.push(alphaBets[alphaBets.indexOf(temp) + 13]) : newString.push(alphaBets[alphaBets.indexOf(tempArr[i]) - 13])) : newString.push(tempArr[i]);
}
return newString.join(""); // Array to String
}
方法6
function isLetter(letter){
if(letter >= 65 && letter <= 90)
return true;
else
return false;
}
function rot13(str) {
var newStr = "";
for(var i=0;i<str.length;i++){
var letter = str.charCodeAt(i);
if(isLetter(letter)){
if(isLetter(letter + 13))
newStr += String.fromCharCode(letter + 13);
else{
var addTo64 = (letter + 13) - 90;
newStr += String.fromCharCode(64 + addTo64);
}
}
else
newStr += String.fromCharCode(letter);
}
return newStr;
}
方法7
function rot13(str){
return (str ? str : this).split('').map(function(_) {
if (!_.match(/[A-Za-z]/)) return _;
c = Math.floor(_.charCodeAt(0) / 97);
k = (_.toLowerCase().charCodeAt(0) - 83) % 26 || 26;
return String.fromCharCode(k + ((c == 0) ? 64 : 96));
}).join('');
}
方法8
function rot13(str) {
return str.replace( /[A-Za-z]/g , function(c) {
return String.fromCharCode( c.charCodeAt(0) + ( c.toUpperCase() <= "M" ? 13 : -13 ) );
} );
// 也可以将上面的代码换成下面这个
// str.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
}
总结
上面通过搜集和整理了实现rot13(str)
的八种方法,虽然每一种方法略有不同,但整个实现思路是一致的。如果你有更好的实现方案欢迎在下面的评论中与我们一起分享。
扩展阅读
- Where is my one-line implementation of rot13 in JavaScript going wrong?
- freeCodeCamp: Caesar's Cipher
- FCC Bonfire Series 148: Caesars Cipher
- Bonfire: Caesar’s Cipher Solution
如需转载,烦请注明出处:http://www.w3cplus.com/javascript/bonfire-caesars-cipher-solution.html