解析JavaScript中的字符串类型与字符编码支持
定义
字符串就是零个或多个排在一起的字符,放在单引号或双引号之中。
'abc' "abc"
'key = "value"' "it's a long journey"
上面两个都是合法的字符串。
如果要在单引号字符串的内部,使用单引号(或者在双引号字符串的内部,使用双引号),就必须在内部的单引号(或者双引号)前面加上反斜杠,用来转义。
'did she say \'hello\'?' // "did she say 'hello'?" "did she say \"hello\"?" // "did she say "hello"?"
由于html语言的属性值使用双引号,所以很多项目约定javascript语言的字符串只使用单引号,本教程就遵守这个约定。当然,只使用双引号也完全可以。重要的是,坚持使用一种风格,不要两种风格混合。
字符串默认只能写在一行内,分成多行将会报错。
'a b c' // syntaxerror: unexpected token illegal
上面代码将一个字符串分成三行,javascript就会报错。
如果长字符串必须分成多行,可以在每一行的尾部使用反斜杠。
var longstring = "long \ long \ long \ string"; longstring // "long long long string"
上面代码表示,加了反斜杠以后,原来写在一行的字符串,可以分成多行书写。但是,输出的时候还是单行,效果与写在同一行完全一样。注意,反斜杠的后面必须是换行符,而不能有其他字符(比如空格),否则会报错。
连接运算符(+)可以连接多个单行字符串,将长字符串拆成多行书写,输出的时候也是单行。
var longstring = 'long ' + 'long ' + 'long ' + 'string';
如果想输出多行字符串,有一种利用多行注释的变通方法。
(function () { /* line 1 line 2 line 3 */}).tostring().split('\n').slice(1, -1).join('\n') // "line 1 // line 2 // line 3"
上面的例子中,输出的字符串就是多行。
转义
反斜杠(\)在字符串内有特殊含义,用来表示一些特殊字符,所以又称为转义符。
需要用反斜杠转义的特殊字符,主要有下面这些:
- \0 null(\u0000)
- \b 后退键(\u0008)
- \f 换页符(\u000c)
- \n 换行符(\u000a)
- \r 回车键(\u000d)
- \t 制表符(\u0009)
- \v 垂直制表符(\u000b)
- \' 单引号(\u0027)
- \" 双引号(\u0022)
- \ 反斜杠(\u005c)
上面这些字符前面加上反斜杠,都表示特殊含义。
console.log('1\n2') // 1 // 2
上面代码中,\n表示换行,输出的时候就分成了两行。
反斜杠还有三种特殊用法。
(1)\hhh
反斜杠后面紧跟三个八进制数(000到377),代表一个字符。hhh对应该字符的unicode码点,比如\251表示版权符号。显然,这种方法只能输出256种字符。
(2)\xhh
\x后面紧跟两个十六进制数(00到ff),代表一个字符。hh对应该字符的unicode码点,比如\xa9表示版权符号。这种方法也只能输出256种字符。
(3)\uxxxx
\u后面紧跟四个十六进制数(0000到ffff),代表一个字符。hhhh对应该字符的unicode码点,比如\u00a9表示版权符号。
下面是这三种字符特殊写法的例子。
'\251' // "©" '\xa9' // "©" '\u00a9' // "©" '\172' === 'z' // true '\x7a' === 'z' // true '\u007a' === 'z' // true
如果在非特殊字符前面使用反斜杠,则反斜杠会被省略。
'\a' // "a"
上面代码中,a是一个正常字符,前面加反斜杠没有特殊含义,反斜杠会被自动省略。
如果字符串的正常内容之中,需要包含反斜杠,则反斜杠前面需要再加一个反斜杠,用来对自身转义。
"prev \\ next" // "prev \ next"
字符串与数组
字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)。
var s = 'hello'; s[0] // "h" s[1] // "e" s[4] // "o" // 直接对字符串使用方括号运算符 'hello'[1] // "e"
如果方括号中的数字超过字符串的长度,或者方括号中根本不是数字,则返回undefined。
'abc'[3] // undefined 'abc'[-1] // undefined 'abc'['x'] // undefined
但是,字符串与数组的相似性仅此而已。实际上,无法改变字符串之中的单个字符。
var s = 'hello'; delete s[0]; s // "hello" s[1] = 'a'; s // "hello" s[5] = '!'; s // "hello"
上面代码表示,字符串内部的单个字符无法改变和增删,这些操作会默默地失败。
字符串之所以类似于字符数组,实际是由于对字符串进行方括号运算时,字符串会自动转换为一个字符串对象。
length属性
length属性返回字符串的长度,该属性也是无法改变的。
var s = 'hello'; s.length // 5 s.length = 3; s.length // 5 s.length = 7; s.length // 5
上面代码表示字符串的length属性无法改变,但是不会报错。
字符集
javascript使用unicode字符集,也就是说在javascript内部,所有字符都用unicode表示。
不仅javascript内部使用unicode储存字符,而且还可以直接在程序中使用unicode,所有字符都可以写成”\uxxxx”的形式,其中xxxx代表该字符的unicode编码。比如,\u00a9代表版权符号。
var s = '\u00a9'; s // "©"
每个字符在javascript内部都是以16位(即2个字节)的utf-16格式储存。也就是说,javascript的单位字符长度固定为16位长度,即2个字节。
但是,utf-16有两种长度:对于u+0000到u+ffff之间的字符,长度为16位(即2个字节);对于u+10000到u+10ffff之间的字符,长度为32位(即4个字节),而且前两个字节在0xd800到0xdbff之间,后两个字节在0xdc00到0xdfff之间。举例来说,u+1d306对应的字符为????,它写成utf-16就是0xd834 0xdf06。浏览器会正确将这四个字节识别为一个字符,但是javascript内部的字符长度总是固定为16位,会把这四个字节视为两个字符。
var s = '\ud834\udf06'; s // "????" s.length // 2 /^.$/.test(s) // false s.charat(0) // "" s.charat(1) // "" s.charcodeat(0) // 55348 s.charcodeat(1) // 57094
上面代码说明,对于于u+10000到u+10ffff之间的字符,javascript总是视为两个字符(字符的length属性为2),用来匹配单个字符的正则表达式会失败(javascript认为这里不止一个字符),charat方法无法返回单个字符,charcodeat方法返回每个字节对应的十进制值。
所以处理的时候,必须把这一点考虑在内。对于4个字节的unicode字符,假定c是字符的unicode编号,h是前两个字节,l是后两个字节,则它们之间的换算关系如下。
// 将大于u+ffff的字符,从unicode转为utf-16 h = math.floor((c - 0x10000) / 0x400) + 0xd800 l = (c - 0x10000) % 0x400 + 0xdc00 // 将大于u+ffff的字符,从utf-16转为unicode c = (h - 0xd800) * 0x400 + l - 0xdc00 + 0x10000
下面的正则表达式可以识别所有utf-16字符。
([\0-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])
由于javascript引擎(严格说是es5规格)不能自动识别辅助平面(编号大于0xffff)的unicode字符,导致所有字符串处理函数遇到这类字符,都会产生错误的结果。如果要完成字符串相关操作,就必须判断字符是否落在0xd800到0xdfff这个区间。
下面是能够正确处理字符串遍历的函数。
function getsymbols(string) { var length = string.length; var index = -1; var output = []; var character; var charcode; while (++index < length) { character = string.charat(index); charcode = character.charcodeat(0); if (charcode >= 0xd800 && charcode <= 0xdbff) { output.push(character + string.charat(++index)); } else { output.push(character); } } return output; } var symbols = getsymbols('????'); symbols.foreach(function(symbol) { // ... });
替换(string.prototype.replace)、截取子字符串(string.prototype.substring, string.prototype.slice)等其他字符串操作,都必须做类似的处理。
base64转码
base64是一种编码方法,可以将任意字符转成可打印字符。使用这种编码方法,主要不是为了加密,而是为了不出现特殊字符,简化程序的处理。
javascript原生提供两个base64相关方法。
- btoa():字符串或二进制值转为base64编码
- atob():base64编码转为原来的编码
var string = 'hello world!'; btoa(string) // "sgvsbg8gv29ybgqh" atob('sgvsbg8gv29ybgqh') // "hello world!" 这两个方法不适合非ascii码的字符,会报错。 btoa('你好') // uncaught domexception: the string to be encoded contains characters outside of the latin1 range. 要将非ascii码字符转为base64编码,必须中间插入一个转码环节,再使用这两个方法。 function b64encode(str) { return btoa(encodeuricomponent(str)); } function b64decode(str) { return decodeuricomponent(atob(str)); } b64encode('你好') // "juu0jujejuewjuu1jue1juje" b64decode('juu0jujejuewjuu1jue1juje') // "你好"
上一篇: JS将unicode码转中文方法
推荐阅读
-
整理HTML5中支持的URL编码与字符编码
-
Java与JavaScript中判断两字符串是否相等的区别
-
解析JavaScript面向对象概念中的Object类型与作用域
-
深入解析JavaScript中的数字对象与字符串对象
-
java字符串在内存和文件中编码的不同,如何理解二进制与编码的关系
-
基于JavaScript中字符串的match与replace方法(详解)
-
全面解析JavaScript中对于字符串子串的查询方法
-
C#中利用LINQ to XML与反射把任意类型的泛型集合转换成XML格式字符串的方法
-
简单总结JavaScript中的String字符串类型
-
解析JavaScript中的字符串类型与字符编码支持