问题描述
输入:
Hello world??????
所需的输出:
??????dlrow olleH
我尝试了几种方法,但是都没有给我正确的答案。
这失败了:
const text = 'Hello world??????';
const reversed = text.split('').reverse().join('');
console.log(reversed);
这有点用,但是它可以将????
分成4种不同的表情符号:
const text = 'Hello world??????';
const reversed = [...text].reverse().join('');
console.log(reversed);
我也尝试了this question中的每个答案,但没有一个起作用。
有没有办法获得想要的输出?
解决方法
如果可以,请使用_.split()
提供的lodash功能。从version 4.0开始,_.split()
可以拆分Unicode表情符号。
使用原生.reverse().join('')
反转“字符”应该可以很好地处理包含零宽度连接符的表情符号
function reverse(txt) { return _.split(txt,'').reverse().join(''); }
const text = 'Hello world??????';
console.log(reverse(text));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>
,
由于许多原因,反转Unicode文本很棘手。
首先,根据编程语言的不同,字符串以不同的方式表示:字节列表,UTF-16代码单元列表(16位宽,在API中通常称为“字符”)或ucs4代码点(4字节宽)。
第二,不同的API在不同程度上反映了内部表示。一些工作于字节的抽象,一些工作于UTF-16字符,一些工作于代码点。当表示形式使用字节或UTF-16字符时,API的通常部分使您可以访问该表示形式的元素,以及执行必要的逻辑以从字节(通过UTF-8)或将UTF-16字符转换为实际代码点。
通常,稍后会添加API中执行该逻辑的部分,从而使您可以访问代码点,因为首先使用7位ascii,然后再后来,每个人都认为使用不同的代码页使用8位就足够了,甚至以后16位就足以用于unicode。历史上,代码点的概念是没有固定上限的整数,这是第四种用于逻辑编码文本的公共字符长度。
使用一个可以访问实际代码点的API就是这样。但是...
第三,有很多修饰语代码点会影响下一个或后面的代码点。例如。有一个变音符号修饰符,将跟随者a转换为ä,e到ë,&c。翻转代码点,然后由不同字母组成的aë变为eä。有一个直接的代表例如ä作为其自己的代码点,但使用修饰符同样有效。
第四,一切都在不断变化。如示例中所使用,表情符号中也有很多修饰符,并且每年还会添加更多的修饰符。因此,如果API使您能够访问代码点是否为修饰符的信息,则API版本将确定它是否已经知道特定的新修饰符。
Unicode仅在视觉外观方面提供了一个技巧:
有书写方向修饰符。在该示例的情况下,使用从左到右的书写方向。只需在文本的开头添加一个从右到左的书写方向修饰符,并且根据API /浏览器的版本,它会正确反转?
'\ u202e'被称为从右到左覆盖,它是从右到左标记的最强版本。
const text = 'Hello world??????'
console.log('\u202e' + text)
const text = 'Hello world??????'
let original = document.getElementById('original')
original.appendChild(document.createTextNode(text))
let result = document.getElementById('result')
result.appendChild(document.createTextNode('\u202e' + text))
body {
font-family: sans-serif
}
<p id="original"></p>
<p id="result"></p>
,
我知道!我将使用RegExp。可能出什么问题了? (答案留给读者练习。)
const text = 'Hello world??????';
const reversed = text.match(/.(\u200d.)*/gu).reverse().join('');
console.log(reversed);
,
替代解决方案是使用runes
库,这是一种小而有效的解决方案:
https://github.com/dotcypress/runes
const runes = require('runes')
// String.substring
'????a'.substring(1) => '�???a'
// Runes
runes.substr('????a',1) => 'a'
runes('12????3?✓').reverse().join();
// results in: "✓?3????21"
,
您不仅遇到了表情符号问题,而且遇到了其他组合字符。 这些感觉像单个字母但实际上是一个或多个Unicode字符的东西称为“扩展字素簇”。
在这些簇中使用字符串比较麻烦(例如,请参见这些unicode docs)。我不会自己依靠它来实现,而是使用现有的库。 Google将我指向grapheme-splitter库。该库的文档包含一些nice examples,可用于大多数实现:
使用此代码,您应该能够:
var splitter = new GraphemeSplitter();
var graphemes = splitter.splitGraphemes(string);
var reversed = graphemes.reverse().join('');
旁听:面向未来的游客或愿意生活在前沿的游客:
有一个proposal可以将一个字素分割器添加到javascript标准中。 (它实际上还提供了其他细分选项)。 目前正在接受第3阶段审核,目前已在JSC和V8中实施(请参见https://github.com/tc39/proposal-intl-segmenter/issues/114)。
使用此代码如下所示:
var segmenter = new Intl.Segmenter("en",{granularity: "grapheme"})
var segment_iterator = segmenter.segment(string)
var graphemes = []
for (let {segment} of segment_iterator) {
graphemes.push(segment)
}
var reversed = graphemes.reverse().join('');
如果您比我了解更多现代的javascript,可能可以更整洁了。
有一个implementation here-但我不知道它需要什么。
注意:这指出了一个有趣的问题,其他答案尚未解决。细分可能取决于您使用的语言环境-而不仅仅是字符串中的字符。
,我只是决定好好玩,这是一个很好的挑战。不确定在所有情况下都是正确的,因此使用后果自负,但是这里是:
function run() {
const text = 'Hello world??????';
const newText = reverseText(text);
console.log(newText);
}
function reverseText(text) {
// first,create an array of characters
let textArray = [...text];
let lastCharConnector = false;
textArray = textArray.reduce((acc,char,index) => {
if (char.charCodeAt(0) === 8205) {
const lastChar = acc[acc.length-1];
if (Array.isArray(lastChar)) {
lastChar.push(char);
} else {
acc[acc.length-1] = [lastChar,char];
}
lastCharConnector = true;
} else if (lastCharConnector) {
acc[acc.length-1].push(char);
lastCharConnector = false;
} else {
acc.push(char);
lastCharConnector = false;
}
return acc;
},[]);
console.log('initial text array',textArray);
textArray = textArray.reverse();
console.log('reversed text array',textArray);
textArray = textArray.map((item) => {
if (Array.isArray(item)) {
return item.join('');
} else {
return item;
}
});
return textArray.join('');
}
run();
,
您可以使用:
yourstring.split('').reverse().join('')
它应该将您的字符串转换为列表,将其反转然后再次使其成为字符串。
,const text ='Hello world??????';
const reversed = text.split('')。reverse()。join('');
console.log(反向);