摘要:
格式化是通过格式操作使任意类型的数据转换成一个字符串。例如下面这样
<script>
console.log(chopper.format('{0} - {1} - {2}', 12, 24, 25)); // outputs "12 - 24 - 25"
</script>
下面是一个完整的代码,可以复制到自己的项目中。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> 5 </head> 6 <body> 7 <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script> 8 <script> 9 (function() { 10 var chopper = window.chopper = window.chopper || { cultures: {} }, 11 math = Math, 12 formatRegExp = /{(d+)(:[^}]+)?}/g, 13 FUNCTION = "function", 14 STRING = "string", 15 NUMBER = "number", 16 OBJECT = "object", 17 NULL = "null", 18 BOOLEAN = "boolean", 19 UNDEFINED = "undefined", 20 slice = [].slice, 21 globalize = window.Globalize, 22 standardFormatRegExp = /^(n|c|p|e)(d*)$/i, 23 literalRegExp = /(\.)|(['][^']*[']?)|(["][^"]*["]?)/g, 24 commaRegExp = /\,/g, 25 EMPTY = "", 26 POINT = ".", 27 COMMA = ",", 28 SHARP = "#", 29 ZERO = "0", 30 PLACEHOLDER = "??", 31 EN = "en-US", 32 objectToString = {}.toString; 33 34 //cultures 35 chopper.cultures["en-US"] = { 36 name: EN, 37 numberFormat: { 38 pattern: ["-n"], 39 decimals: 2, 40 ",": ",", 41 ".": ".", 42 groupSize: [3], 43 percent: { 44 pattern: ["-n %", "n %"], 45 decimals: 2, 46 ",": ",", 47 ".": ".", 48 groupSize: [3], 49 symbol: "%" 50 }, 51 currency: { 52 pattern: ["($n)", "$n"], 53 decimals: 2, 54 ",": ",", 55 ".": ".", 56 groupSize: [3], 57 symbol: "$" 58 } 59 }, 60 calendars: { 61 standard: { 62 days: { 63 names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], 64 namesAbbr: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], 65 namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ] 66 }, 67 months: { 68 names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], 69 namesAbbr: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 70 }, 71 AM: [ "AM", "am", "AM" ], 72 PM: [ "PM", "pm", "PM" ], 73 patterns: { 74 d: "M/d/yyyy", 75 D: "dddd, MMMM dd, yyyy", 76 F: "dddd, MMMM dd, yyyy h:mm:ss tt", 77 g: "M/d/yyyy h:mm tt", 78 G: "M/d/yyyy h:mm:ss tt", 79 m: "MMMM dd", 80 M: "MMMM dd", 81 s: "yyyy'-'MM'-'ddTHH':'mm':'ss", 82 t: "h:mm tt", 83 T: "h:mm:ss tt", 84 u: "yyyy'-'MM'-'dd HH':'mm':'ss'Z'", 85 y: "MMMM, yyyy", 86 Y: "MMMM, yyyy" 87 }, 88 "/": "/", 89 ":": ":", 90 firstDay: 0, 91 twoDigitYearMax: 2029 92 } 93 } 94 }; 95 96 97 function findCulture(culture) { 98 if (culture) { 99 if (culture.numberFormat) { 100 return culture; 101 } 102 103 if (typeof culture === STRING) { 104 var cultures = chopper.cultures; 105 return cultures[culture] || cultures[culture.split("-")[0]] || null; 106 } 107 108 return null; 109 } 110 111 return null; 112 } 113 114 function getCulture(culture) { 115 if (culture) { 116 culture = findCulture(culture); 117 } 118 119 return culture || chopper.cultures.current; 120 } 121 122 function expandNumberFormat(numberFormat) { 123 numberFormat.groupSizes = numberFormat.groupSize; 124 numberFormat.percent.groupSizes = numberFormat.percent.groupSize; 125 numberFormat.currency.groupSizes = numberFormat.currency.groupSize; 126 } 127 128 chopper.culture = function(cultureName) { 129 var cultures = chopper.cultures, culture; 130 131 if (cultureName !== undefined) { 132 culture = findCulture(cultureName) || cultures[EN]; 133 culture.calendar = culture.calendars.standard; 134 cultures.current = culture; 135 136 if (globalize && !globalize.load) { 137 expandNumberFormat(culture.numberFormat); 138 } 139 140 } else { 141 return cultures.current; 142 } 143 }; 144 145 146 chopper.culture(EN); 147 148 //number formatting 149 function formatNumber(number, format, culture) { 150 culture = getCulture(culture); 151 152 var numberFormat = culture.numberFormat, 153 groupSize = numberFormat.groupSize[0], 154 groupSeparator = numberFormat[COMMA], 155 decimal = numberFormat[POINT], 156 precision = numberFormat.decimals, 157 pattern = numberFormat.pattern[0], 158 literals = [], 159 symbol, 160 isCurrency, isPercent, 161 customPrecision, 162 formatAndPrecision, 163 negative = number < 0, 164 integer, 165 fraction, 166 integerLength, 167 fractionLength, 168 replacement = EMPTY, 169 value = EMPTY, 170 idx, 171 length, 172 ch, 173 hasGroup, 174 hasNegativeFormat, 175 decimalIndex, 176 sharpIndex, 177 zeroIndex, 178 hasZero, hasSharp, 179 percentIndex, 180 currencyIndex, 181 startZeroIndex, 182 start = -1, 183 end; 184 185 //return empty string if no number 186 if (number === undefined) { 187 return EMPTY; 188 } 189 190 if (!isFinite(number)) { 191 return number; 192 } 193 194 //if no format then return number.toString() or number.toLocaleString() if culture.name is not defined 195 if (!format) { 196 return culture.name.length ? number.toLocaleString() : number.toString(); 197 } 198 199 formatAndPrecision = standardFormatRegExp.exec(format); 200 201 // standard formatting 202 if (formatAndPrecision) { 203 format = formatAndPrecision[1].toLowerCase(); 204 205 isCurrency = format === "c"; 206 isPercent = format === "p"; 207 208 if (isCurrency || isPercent) { 209 //get specific number format information if format is currency or percent 210 numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent; 211 groupSize = numberFormat.groupSize[0]; 212 groupSeparator = numberFormat[COMMA]; 213 decimal = numberFormat[POINT]; 214 precision = numberFormat.decimals; 215 symbol = numberFormat.symbol; 216 pattern = numberFormat.pattern[negative ? 0 : 1]; 217 } 218 219 customPrecision = formatAndPrecision[2]; 220 221 if (customPrecision) { 222 precision = +customPrecision; 223 } 224 225 //return number in exponential format 226 if (format === "e") { 227 return customPrecision ? number.toExponential(precision) : number.toExponential(); // toExponential() and toExponential(undefined) differ in FF #653438. 228 } 229 230 // multiply if format is percent 231 if (isPercent) { 232 number *= 100; 233 } 234 235 number = round(number, precision); 236 negative = number < 0; 237 number = number.split(POINT); 238 239 integer = number[0]; 240 fraction = number[1]; 241 242 //exclude "-" if number is negative. 243 if (negative) { 244 integer = integer.substring(1); 245 } 246 247 value = integer; 248 integerLength = integer.length; 249 250 //add group separator to the number if it is longer enough 251 if (integerLength >= groupSize) { 252 value = EMPTY; 253 for (idx = 0; idx < integerLength; idx++) { 254 if (idx > 0 && (integerLength - idx) % groupSize === 0) { 255 value += groupSeparator; 256 } 257 value += integer.charAt(idx); 258 } 259 } 260 261 if (fraction) { 262 value += decimal + fraction; 263 } 264 265 if (format === "n" && !negative) { 266 return value; 267 } 268 269 number = EMPTY; 270 271 for (idx = 0, length = pattern.length; idx < length; idx++) { 272 ch = pattern.charAt(idx); 273 274 if (ch === "n") { 275 number += value; 276 } else if (ch === "$" || ch === "%") { 277 number += symbol; 278 } else { 279 number += ch; 280 } 281 } 282 283 return number; 284 } 285 286 //custom formatting 287 // 288 //separate format by sections. 289 290 //make number positive 291 if (negative) { 292 number = -number; 293 } 294 295 if (format.indexOf("'") > -1 || format.indexOf(""") > -1 || format.indexOf("\") > -1) { 296 format = format.replace(literalRegExp, function (match) { 297 var quoteChar = match.charAt(0).replace("\", ""), 298 literal = match.slice(1).replace(quoteChar, ""); 299 300 literals.push(literal); 301 302 return PLACEHOLDER; 303 }); 304 } 305 306 format = format.split(";"); 307 if (negative && format[1]) { 308 //get negative format 309 format = format[1]; 310 hasNegativeFormat = true; 311 } else if (number === 0) { 312 //format for zeros 313 format = format[2] || format[0]; 314 if (format.indexOf(SHARP) == -1 && format.indexOf(ZERO) == -1) { 315 //return format if it is string constant. 316 return format; 317 } 318 } else { 319 format = format[0]; 320 } 321 322 percentIndex = format.indexOf("%"); 323 currencyIndex = format.indexOf("$"); 324 325 isPercent = percentIndex != -1; 326 isCurrency = currencyIndex != -1; 327 328 //multiply number if the format has percent 329 if (isPercent) { 330 number *= 100; 331 } 332 333 if (isCurrency && format[currencyIndex - 1] === "\") { 334 format = format.split("\").join(""); 335 isCurrency = false; 336 } 337 338 if (isCurrency || isPercent) { 339 //get specific number format information if format is currency or percent 340 numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent; 341 groupSize = numberFormat.groupSize[0]; 342 groupSeparator = numberFormat[COMMA]; 343 decimal = numberFormat[POINT]; 344 precision = numberFormat.decimals; 345 symbol = numberFormat.symbol; 346 } 347 348 hasGroup = format.indexOf(COMMA) > -1; 349 if (hasGroup) { 350 format = format.replace(commaRegExp, EMPTY); 351 } 352 353 decimalIndex = format.indexOf(POINT); 354 length = format.length; 355 356 if (decimalIndex != -1) { 357 fraction = number.toString().split("e"); 358 if (fraction[1]) { 359 fraction = round(number, Math.abs(fraction[1])); 360 } else { 361 fraction = fraction[0]; 362 } 363 fraction = fraction.split(POINT)[1] || EMPTY; 364 zeroIndex = format.lastIndexOf(ZERO) - decimalIndex; 365 sharpIndex = format.lastIndexOf(SHARP) - decimalIndex; 366 hasZero = zeroIndex > -1; 367 hasSharp = sharpIndex > -1; 368 idx = fraction.length; 369 370 if (!hasZero && !hasSharp) { 371 format = format.substring(0, decimalIndex) + format.substring(decimalIndex + 1); 372 length = format.length; 373 decimalIndex = -1; 374 idx = 0; 375 } if (hasZero && zeroIndex > sharpIndex) { 376 idx = zeroIndex; 377 } else if (sharpIndex > zeroIndex) { 378 if (hasSharp && idx > sharpIndex) { 379 idx = sharpIndex; 380 } else if (hasZero && idx < zeroIndex) { 381 idx = zeroIndex; 382 } 383 } 384 385 if (idx > -1) { 386 number = round(number, idx); 387 } 388 } else { 389 number = round(number); 390 } 391 392 sharpIndex = format.indexOf(SHARP); 393 startZeroIndex = zeroIndex = format.indexOf(ZERO); 394 395 //define the index of the first digit placeholder 396 if (sharpIndex == -1 && zeroIndex != -1) { 397 start = zeroIndex; 398 } else if (sharpIndex != -1 && zeroIndex == -1) { 399 start = sharpIndex; 400 } else { 401 start = sharpIndex > zeroIndex ? zeroIndex : sharpIndex; 402 } 403 404 sharpIndex = format.lastIndexOf(SHARP); 405 zeroIndex = format.lastIndexOf(ZERO); 406 407 //define the index of the last digit placeholder 408 if (sharpIndex == -1 && zeroIndex != -1) { 409 end = zeroIndex; 410 } else if (sharpIndex != -1 && zeroIndex == -1) { 411 end = sharpIndex; 412 } else { 413 end = sharpIndex > zeroIndex ? sharpIndex : zeroIndex; 414 } 415 416 if (start == length) { 417 end = start; 418 } 419 420 if (start != -1) { 421 value = number.toString().split(POINT); 422 integer = value[0]; 423 fraction = value[1] || EMPTY; 424 425 integerLength = integer.length; 426 fractionLength = fraction.length; 427 428 if (negative && (number * -1) >= 0) { 429 negative = false; 430 } 431 432 //add group separator to the number if it is longer enough 433 if (hasGroup) { 434 if (integerLength === groupSize && integerLength < decimalIndex - startZeroIndex) { 435 integer = groupSeparator + integer; 436 } else if (integerLength > groupSize) { 437 value = EMPTY; 438 for (idx = 0; idx < integerLength; idx++) { 439 if (idx > 0 && (integerLength - idx) % groupSize === 0) { 440 value += groupSeparator; 441 } 442 value += integer.charAt(idx); 443 } 444 445 integer = value; 446 } 447 } 448 449 number = format.substring(0, start); 450 451 if (negative && !hasNegativeFormat) { 452 number += "-"; 453 } 454 455 for (idx = start; idx < length; idx++) { 456 ch = format.charAt(idx); 457 458 if (decimalIndex == -1) { 459 if (end - idx < integerLength) { 460 number += integer; 461 break; 462 } 463 } else { 464 if (zeroIndex != -1 && zeroIndex < idx) { 465 replacement = EMPTY; 466 } 467 468 if ((decimalIndex - idx) <= integerLength && decimalIndex - idx > -1) { 469 number += integer; 470 idx = decimalIndex; 471 } 472 473 if (decimalIndex === idx) { 474 number += (fraction ? decimal : EMPTY) + fraction; 475 idx += end - decimalIndex + 1; 476 continue; 477 } 478 } 479 480 if (ch === ZERO) { 481 number += ch; 482 replacement = ch; 483 } else if (ch === SHARP) { 484 number += replacement; 485 } 486 } 487 488 if (end >= start) { 489 number += format.substring(end + 1); 490 } 491 492 //replace symbol placeholders 493 if (isCurrency || isPercent) { 494 value = EMPTY; 495 for (idx = 0, length = number.length; idx < length; idx++) { 496 ch = number.charAt(idx); 497 value += (ch === "$" || ch === "%") ? symbol : ch; 498 } 499 number = value; 500 } 501 502 length = literals.length; 503 504 if (length) { 505 for (idx = 0; idx < length; idx++) { 506 number = number.replace(PLACEHOLDER, literals[idx]); 507 } 508 } 509 } 510 511 return number; 512 } 513 514 var round = function(value, precision) { 515 precision = precision || 0; 516 517 value = value.toString().split('e'); 518 value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + precision) : precision))); 519 520 value = value.toString().split('e'); 521 value = +(value[0] + 'e' + (value[1] ? (+value[1] - precision) : -precision)); 522 523 return value.toFixed(precision); 524 }; 525 526 var toString = function(value, fmt, culture) { 527 if (fmt) { 528 if (typeof value === NUMBER) { 529 return formatNumber(value, fmt, culture); 530 } 531 } 532 533 return value !== undefined ? value : ""; 534 }; 535 536 if (globalize && !globalize.load) { 537 toString = function(value, format, culture) { 538 if ($.isPlainObject(culture)) { 539 culture = culture.name; 540 } 541 542 return globalize.format(value, format, culture); 543 }; 544 } 545 546 chopper.format = function(fmt) { 547 var values = arguments; 548 549 return fmt.replace(formatRegExp, function(match, index, placeholderFormat) { 550 var value = values[parseInt(index, 10) + 1]; 551 552 return toString(value, placeholderFormat ? placeholderFormat.substring(1) : ""); 553 }); 554 }; 555 })(); 556 </script> 557 </body> 558 </html>
API:
chopper.format('{0} is playing {1}', 'Xiaoming', 'basketball'); // outputs "Xiaoming is playing basketball"
// 价格
chopper.format('{0:c} - {1:c}', 10, 20); // outputs "$10.00 - $20.00"
// 指数
chopper.format('指数: {0:e}', 25); // outputs "指数: 2.5e+1"
// 百分数
chopper.format('百分数: {0:p}', 25); // outputs "百分数: 2,500.00 %"
// 小数
chopper.format('小数: {0:n}', 25); // outputs "小数: 25.00"
小结:
开发中格式化数据还是经常用到的,比如我们要根据变量提示不同的信息,但是内容模板都是一样的,这样的话我们就可以使用此方法。如果你的项目使用jQuery,你也可以将上面的javascript封装成jQuery插件。