Created
February 15, 2014 01:42
-
-
Save luiscubal/9013217 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Tanto a resposta do Gabriel Santos como a do mgibsonbr estão erradas (embora funcionem bem nalguns casos limitados que acabam por ser os mais comuns). | |
| **Porque motivo estão errados?** | |
| Unicode. Nos tempos ancestrais (ASCII), cada caracter ocupava um espaço constante, mas isso já não é verdade. | |
| O JavaScript usa o sistema UTF-16, em que aquilo a que a maioria das pessoas associa a caracter é dividido em "code points", que por sua vez são divididos em "code units". | |
| Um caracter pode ter um ou mais code point e cada code point pode ter um ou dois code units. O primeiro é o code point "principal" e os seguintes (opcionais) são modificadores que acrescentam algo ao caracter (exemplo: acentos). | |
| Quando um code point ocupa dois code units em UTF-16, diz-se que o primeiro é o "High Surrogate" (ou lead surrogate) e o segundo é o "Low Surrogate" (ou trail surrogate). | |
| Exemplo de um caracter que falha nas respostas apresentas: o caracter 𝒞͆. | |
| O caracter que eu coloquei é constituido por dois code points: O caracter 𝒞 e o modificador ͆ . | |
| O code point 𝒞, por sua vez, ocupa dois code units. | |
| Se calcular o inverso de "Olá 𝒞 ͆!" com as outras funções vai obter !͆ �� ́alO, que é claramente o resultado errado. | |
| **A solução** | |
| A solução completa ([jsfiddle de teste][1]) é a seguinte: | |
| String.prototype.isHighSurrogate = function () { | |
| var charCode = this.charCodeAt(0); | |
| return charCode >= 0xD800 && charCode <= 0xDBFF; | |
| } | |
| String.prototype.isCombining = function () { | |
| if (this.length != 1) { | |
| //Todos os caracteres de combinação estão no BMP | |
| return false; | |
| } | |
| var codePoint = this.charCodeAt(0); | |
| //Combining Diacritical Marks | |
| if (codePoint >= 0x0300 && codePoint <= 0x036F) | |
| return true; | |
| //Combining Diacritical Marks Supplement | |
| if (codePoint >= 0x1DC0 && codePoint <= 0x1DFF) | |
| return true; | |
| //Combining Diacritical Marks for Symbols | |
| if (codePoint >= 0x20D0 && codePoint <= 0x20FF) | |
| return true; | |
| //Combining Half Marks | |
| if (codePoint >= 0xFE20 && codePoint <= 0xFE2F) | |
| return true; | |
| return false; | |
| } | |
| String.prototype.codePoints = function () { | |
| var codePoints = []; | |
| var currentPoint = ''; | |
| for (var i = 0; i < this.length; ++i) { | |
| var currentUnit = this[i]; | |
| currentPoint += currentUnit; | |
| if (!currentUnit.isHighSurrogate()) { | |
| currentPoint = currentPoint; | |
| codePoints.push(currentPoint); | |
| currentPoint = ''; | |
| } | |
| } | |
| return codePoints; | |
| } | |
| String.prototype.chars = function () { | |
| var chars = []; | |
| var codePoints = this.codePoints(); | |
| var currentChar = ''; | |
| for (var i = 0; i < codePoints.length; ++i) { | |
| var codePoint = codePoints[i]; | |
| if (!codePoint.isCombining()) { | |
| chars.push(currentChar); | |
| currentChar = ''; | |
| } | |
| currentChar += codePoint; | |
| } | |
| chars.push(currentChar); | |
| return chars; | |
| } | |
| String.prototype.reverse = function () { | |
| return this.chars().reverse().join(''); | |
| } | |
| Para usar o código acima, use `"String a inverter".reverse()`. | |
| [1]: http://jsfiddle.net/E6yb8/1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment