如何反转包含复杂表情符号的字符串? 输入:所需的输出:

问题描述

输入:

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'被称为从右到左覆盖,它是从右到左标记的最强版本。

请参见this explanation by w3.org

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(反向);