PerchΓ© i caratteri emoji come πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦ vengono trattati in modo cosΓ¬ strano nelle corde Swift?


540

Il personaggio πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦ (famiglia con due donne, una ragazza e un ragazzo) Γ¨ codificato come tale:

U+1F469 WOMAN,
‍U+200D ZWJ,
U+1F469 WOMAN,
U+200D ZWJ,
U+1F467 GIRL,
U+200D ZWJ,
U+1F466 BOY

Quindi Γ¨ codificato in modo molto interessante; l'obiettivo perfetto per un test unitario. Tuttavia, Swift non sembra sapere come trattarlo. Ecco cosa intendo:

"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦") // true
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘©") // false
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("\u{200D}") // false
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘§") // false
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘¦") // true

Quindi, Swift dice che contiene se stesso (buono) e un ragazzo (buono!). Ma poi dice che non contiene una donna, una ragazza o un falegname a larghezza zero. Cosa sta succedendo qui? PerchΓ© Swift sa che contiene un ragazzo ma non una donna o una ragazza? Potrei capire se lo trattasse come un singolo personaggio e lo riconoscesse solo contenendo se stesso, ma il fatto che abbia ottenuto un sottocomponente e nessun altro mi sconcerta.

Questo non cambia se uso qualcosa del genere "πŸ‘©".characters.first!.


Ancora piΓΉ confuso Γ¨ questo:

let manual = "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}"
Array(manual.characters) // ["πŸ‘©β€", "πŸ‘©β€", "πŸ‘§β€", "πŸ‘¦"]

Anche se ho inserito gli ZWJ, questi non si riflettono nella serie di caratteri. Quello che seguì fu un piccolo racconto:

manual.contains("πŸ‘©") // false
manual.contains("πŸ‘§") // false
manual.contains("πŸ‘¦") // true

Quindi ottengo lo stesso comportamento con l'array di caratteri ... il che Γ¨ estremamente fastidioso, poichΓ© so che aspetto ha l'array.

Anche questo non cambia se uso qualcosa del genere "πŸ‘©".characters.first!.



1
I commenti non sono per una discussione estesa; questa conversazione Γ¨ stata spostata in chat .
Martijn Pieters

1
Risolto in Swift 4. "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("\u{200D}")restituisce comunque false, non sono sicuro che si tratti di un bug o di una funzione.
Kevin,

4
Yikes. Unicode ha rovinato il testo. Ha trasformato il testo normale in un linguaggio di markup.
Boann,

6
@Boann sì e no ... molte di queste modifiche sono state apportate per rendere / decodificare cose come Hangul Jamo (255 punti di codice) non un incubo assoluto come lo era per Kanji (13.108 punti di codice) e ideografi cinesi (199.528 punti di codice). Certo, è più complicato e interessante di quanto la lunghezza di un commento SO possa consentire, quindi ti incoraggio a verificarlo tu stesso: D
Ben Leggiero

Risposte:


402

Questo ha a che fare con il modo in cui il Stringtipo funziona in Swift e come funziona il contains(_:)metodo.

'πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦' Γ¨ ciΓ² che Γ¨ noto come una sequenza emoji, che viene visualizzato come un carattere visibile in una stringa. La sequenza Γ¨ composta da Characteroggetti e allo stesso tempo Γ¨ composta da UnicodeScalaroggetti.

Se controlli il conteggio dei caratteri della stringa, vedrai che Γ¨ composto da quattro caratteri, mentre se controlli il conteggio scalare unicode, ti mostrerΓ  un risultato diverso:

print("πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".characters.count)     // 4
print("πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".unicodeScalars.count) // 7

Ora, se analizzi i personaggi e li stampi, vedrai quelli che sembrano personaggi normali, ma in realtΓ  i primi tre personaggi contengono sia un'emoji che un falegname a larghezza zero nei loro UnicodeScalarView:

for char in "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".characters {
    print(char)

    let scalars = String(char).unicodeScalars.map({ String($0.value, radix: 16) })
    print(scalars)
}

// πŸ‘©β€
// ["1f469", "200d"]
// πŸ‘©β€
// ["1f469", "200d"]
// πŸ‘§β€
// ["1f467", "200d"]
// πŸ‘¦
// ["1f466"]

Come puoi vedere, solo l'ultimo carattere non contiene un falegname a larghezza zero, quindi quando si utilizza il contains(_:)metodo, funziona come previsto. Dal momento che non stai confrontando con emoji contenenti joiner a larghezza zero, il metodo non troverΓ  una corrispondenza per nessuno tranne l'ultimo personaggio.

Per espanderlo, se crei un carattere Stringcomposto da un personaggio emoji che termina con un joiner di larghezza zero e lo passi al contains(_:)metodo, lo valuterΓ  anche a false. Questo ha a che fare con l' contains(_:)essere esattamente lo stesso range(of:) != nil, che cerca di trovare una corrispondenza esatta con l'argomento dato. PoichΓ© i caratteri che terminano con un falegname a larghezza zero formano una sequenza incompleta, il metodo tenta di trovare una corrispondenza per l'argomento combinando i caratteri che terminano con un falegname a larghezza zero in una sequenza completa. CiΓ² significa che il metodo non troverΓ  mai una corrispondenza se:

  1. l'argomento termina con un joiner di larghezza zero e
  2. la stringa da analizzare non contiene una sequenza incompleta (cioè termina con un joiner di larghezza zero e non seguita da un carattere compatibile).

Dimostrare:

let s = "\u{1f469}\u{200d}\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}" // πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦

s.range(of: "\u{1f469}\u{200d}") != nil                            // false
s.range(of: "\u{1f469}\u{200d}\u{1f469}") != nil                   // false

Tuttavia, poichΓ© il confronto guarda solo avanti, puoi trovare diverse altre sequenze complete all'interno della stringa lavorando all'indietro:

s.range(of: "\u{1f466}") != nil                                    // true
s.range(of: "\u{1f467}\u{200d}\u{1f466}") != nil                   // true
s.range(of: "\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}") != nil  // true

// Same as the above:
s.contains("\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}")          // true

La soluzione piΓΉ semplice sarebbe quella di fornire un'opzione di confronto specifica al range(of:options:range:locale:)metodo. L'opzione String.CompareOptions.literalesegue il confronto su un'equivalenza esatta carattere per carattere . Come nota a margine, ciΓ² che qui si intende per carattere non Γ¨ Swift Character, ma la rappresentazione UTF-16 sia dell'istanza che della stringa di confronto - tuttavia, poichΓ© Stringnon consente UTF-16 non valido, ciΓ² equivale essenzialmente al confronto dello scalare Unicode rappresentazione.

Qui ho sovraccaricato il Foundationmetodo, quindi se hai bisogno di quello originale, rinomina questo o qualcosa del genere:

extension String {
    func contains(_ string: String) -> Bool {
        return self.range(of: string, options: String.CompareOptions.literal) != nil
    }
}

Ora il metodo funziona come "dovrebbe" con ogni personaggio, anche con sequenze incomplete:

s.contains("πŸ‘©")          // true
s.contains("πŸ‘©\u{200d}")  // true
s.contains("\u{200d}")    // true

47
@MartinR Secondo l'attuale UTR29 (Unicode 9.0), Γ¨ un cluster grapheme esteso ( regole GB10 e GB11 ), ma Swift utilizza chiaramente una versione precedente. Apparentemente risolve questo obiettivo per la versione 4 della lingua , quindi questo comportamento cambierΓ  in futuro.
Michael Homer,

9
@MichaelHomer: A quanto pare Γ¨ stato risolto, viene "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".countvalutato 1con l'attuale Xcode 9 beta e Swift 4.
Martin R

5
Wow. Questo Γ¨ eccellente Ma ora sto diventando nostalgico per i vecchi tempi quando il peggior problema che ho avuto con le stringhe Γ¨ se usano codifiche in stile C o Pascal.
Owen Godfrey,

2
Capisco perchΓ© lo standard Unicode potrebbe aver bisogno di supportare questo, ma amico, questo Γ¨ un disastro troppo ingegnoso, se non altro: /
Ripristina Monica il

110

Il primo problema Γ¨ che stai collegando a Foundation con contains(Swift's Stringnon Γ¨ un Collection), quindi questo Γ¨ un NSStringcomportamento, che non credo gestisce Emoji composte con la stessa potenza di Swift. Detto questo, Swift credo stia implementando Unicode 8 in questo momento, che ha anche bisogno di essere rivisto in merito a questa situazione in Unicode 10 (quindi tutto ciΓ² potrebbe cambiare quando implementano Unicode 10; non ho capito se lo farΓ  o meno).

Per semplificare la cosa, sbarazziamoci di Foundation e usiamo Swift, che offre viste piΓΉ esplicite. Inizieremo con i personaggi:

"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".characters.forEach { print($0) }
πŸ‘©β€
πŸ‘©β€
πŸ‘§β€
πŸ‘¦

OK. Questo Γ¨ quello che ci aspettavamo. Ma Γ¨ una bugia. Vediamo cosa sono veramente quei personaggi.

"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".characters.forEach { print(String($0).unicodeScalars.map{$0}) }
["\u{0001F469}", "\u{200D}"]
["\u{0001F469}", "\u{200D}"]
["\u{0001F467}", "\u{200D}"]
["\u{0001F466}"]

Ah ... Quindi lo Γ¨ ["πŸ‘©ZWJ", "πŸ‘©ZWJ", "πŸ‘§ZWJ", "πŸ‘¦"]. Questo rende tutto un po 'piΓΉ chiaro. πŸ‘© non Γ¨ un membro di questo elenco (Γ¨ "πŸ‘©ZWJ"), ma πŸ‘¦ Γ¨ un membro.

Il problema Γ¨ che si Charactertratta di un "cluster grapheme", che compone le cose insieme (come collegare lo ZWJ). Quello che stai davvero cercando Γ¨ uno scalare unicode. E funziona esattamente come ti aspetti:

"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".unicodeScalars.contains("πŸ‘©") // true
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".unicodeScalars.contains("\u{200D}") // true
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".unicodeScalars.contains("πŸ‘§") // true
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".unicodeScalars.contains("πŸ‘¦") // true

E ovviamente possiamo anche cercare il personaggio reale che è lì dentro:

"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".characters.contains("πŸ‘©\u{200D}") // true

(Questo duplica pesantemente i punti di Ben Leggiero. L'ho pubblicato prima di notare che aveva risposto. Lasciare nel caso sia piΓΉ chiaro per chiunque.)


Cosa significa ZWJ?
LinusGeffarth,

2
Falegname Zero Width
Rob Napier,

@RobNapier in Swift 4, Γ¨ Stringstato presumibilmente cambiato di nuovo in un tipo di raccolta. CiΓ² influisce affatto sulla tua risposta?
Ben Leggiero,

No. Questo ha cambiato cose come la sottoscrizione. Non ha cambiato il funzionamento dei personaggi.
Rob Napier,

75

Sembra che Swift consideri ZWJa un cluster grapheme esteso con il personaggio che lo precede immediatamente. Possiamo vederlo quando mappiamo la matrice di caratteri nei loro unicodeScalars:

Array(manual.characters).map { $0.description.unicodeScalars }

Questo stampa quanto segue da LLDB:

β–Ώ 4 elements
  β–Ώ 0 : StringUnicodeScalarView("πŸ‘©β€")
    - 0 : "\u{0001F469}"
    - 1 : "\u{200D}"
  β–Ώ 1 : StringUnicodeScalarView("πŸ‘©β€")
    - 0 : "\u{0001F469}"
    - 1 : "\u{200D}"
  β–Ώ 2 : StringUnicodeScalarView("πŸ‘§β€")
    - 0 : "\u{0001F467}"
    - 1 : "\u{200D}"
  β–Ώ 3 : StringUnicodeScalarView("πŸ‘¦")
    - 0 : "\u{0001F466}"

Inoltre, i .containsgruppi hanno esteso i grappoli in un singolo personaggio. Per esempio, prendendo i caratteri Hangul α„’, α…‘, e ᆫ(che si combinano per rendere la parola coreano per "uno": ᄒᅑᆫ):

"\u{1112}\u{1161}\u{11AB}".contains("\u{1112}") // false

CiΓ² non Γ¨ stato possibile in α„’quanto i tre punti di codice sono raggruppati in un cluster che funge da unico carattere. Allo stesso modo, \u{1F469}\u{200D}( WOMAN ZWJ) Γ¨ un cluster, che funge da unico carattere.


19

Le altre risposte discutono di ciΓ² che Swift fa, ma non entrano in molti dettagli sul perchΓ©.

Ti aspetti che "Γ…" sia uguale a "Γ…"? Mi aspetto che lo faresti.

Uno di questi Γ¨ una lettera con un combinatore, l'altro Γ¨ un singolo personaggio composto. Puoi aggiungere molti combinatori diversi a un personaggio base e un essere umano lo considererebbe comunque come un singolo personaggio. Per far fronte a questo tipo di discrepanza Γ¨ stato creato il concetto di un grafema per rappresentare ciΓ² che un essere umano considererebbe un personaggio indipendentemente dai punti di codice utilizzati.

Ora i servizi di messaggistica di testo combinano i caratteri in emoji grafici da anni :)Β β†’Β  πŸ™‚. Quindi diverse emoji sono state aggiunte a Unicode.
Questi servizi hanno anche iniziato a combinare le emoji in emoji composite.
Naturalmente non esiste un modo ragionevole per codificare tutte le possibili combinazioni in singoli punti di codice, quindi il Consorzio Unicode ha deciso di espandere il concetto di grafemi per includere questi caratteri compositi.

CiΓ² che si riduce a ciΓ² "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦"dovrebbe essere considerato come un singolo "grapheme cluster" se si tenta di lavorare con esso a livello di grapheme, come Swift fa per impostazione predefinita.

Se vuoi controllare se contiene "πŸ‘¦"come parte di quello, allora dovresti scendere ad un livello inferiore.


Non conosco la sintassi di Swift, quindi ecco alcuni Perl 6 che hanno un livello simile di supporto per Unicode.
(Perl 6 supporta Unicode versione 9, quindi potrebbero esserci delle discrepanze)

say "\c[family: woman woman girl boy]" eq "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦"; # True

# .contains is a Str method only, in Perl 6
say "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦")    # True
say "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘¦");        # False
say "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("\x[200D]");  # False

# comb with no arguments splits a Str into graphemes
my @graphemes = "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".comb;
say @graphemes.elems;                # 1

Scendiamo di livello

# look at it as a list of NFC codepoints
my @components := "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".NFC;
say @components.elems;                     # 7

say @components.grep("πŸ‘¦".ord).Bool;       # True
say @components.grep("\x[200D]".ord).Bool; # True
say @components.grep(0x200D).Bool;         # True

Scendere a questo livello puΓ² rendere alcune cose piΓΉ difficili.

my @match = "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".ords;
my $l = @match.elems;
say @components.rotor( $l => 1-$l ).grep(@match).Bool; # True

Presumo che .containsin Swift sia piΓΉ facile, ma ciΓ² non significa che non ci siano altre cose che diventano piΓΉ difficili.

Lavorare a questo livello rende molto piΓΉ semplice dividere accidentalmente una stringa nel mezzo di un carattere composto, ad esempio.


CiΓ² che si chiede inavvertitamente Γ¨ perchΓ© questa rappresentazione di livello superiore non funziona come una rappresentazione di livello inferiore. La risposta Γ¨ ovviamente, non dovrebbe.

Se ti stai chiedendo " perché questo deve essere così complicato ", la risposta è ovviamente " umani ".


4
Mi hai perso nel tuo ultimo esempio; cosa fanno rotore grepcosa fanno qui? E cos'Γ¨ 1-$l?
Ben Leggiero,

4
Il termine "grafema" ha almeno 50 anni. Unicode lo introdusse allo standard perchΓ© avevano giΓ  usato il termine "personaggio" per significare qualcosa di molto diverso da quello che si pensa normalmente come personaggio. Riesco a leggere ciΓ² che hai scritto come coerente con quello, ma sospetto che altri possano avere l'impressione sbagliata, quindi questo commento (si spera chiarisce).
raiph,

2
@BenLeggiero In primo luogo, rotor. Il codice say (1,2,3,4,5,6).rotor(3)cede ((1 2 3) (4 5 6)). Questo Γ¨ un elenco di elenchi, ogni lunghezza 3. say (1,2,3,4,5,6).rotor(3=>-2)produce lo stesso, tranne per il fatto che il secondo elenco secondario inizia con 2anzichΓ© con 4il terzo 3, e cosΓ¬ via, con rendimento ((1 2 3) (2 3 4) (3 4 5) (4 5 6)). Se @matchcontiene, "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".ordsil codice di @ Brad crea solo un elenco secondario, quindi il =>1-$lbit Γ¨ irrilevante (non utilizzato). È rilevante solo se @matchΓ¨ piΓΉ corto di @components.
raiph,

1
grepcerca di abbinare ogni elemento nel suo invocante (in questo caso, un elenco di liste secondarie di @components). Cerca di abbinare ciascun elemento al suo argomento matcher (in questo caso, @match). I .Boolrendimenti poi TrueIFF l' grepproduce almeno una partita.
raiph,

18

Aggiornamento di Swift 4.0

String ha ricevuto molte revisioni nell'aggiornamento di Swift 4, come documentato in SE-0163 . Per questa demo vengono utilizzate due emoji che rappresentano due diverse strutture. Entrambi sono combinati con una sequenza di emoji.

πŸ‘πŸ½Γ¨ la combinazione di due emoji πŸ‘e🏽

πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦Γ¨ la combinazione di quattro emoji, con joiner a larghezza zero collegato. Il formato Γ¨πŸ‘©β€joinerπŸ‘©β€joinerπŸ‘§β€joinerπŸ‘¦

1. Conta

In Swift 4.0 l'emoji viene conteggiata come cluster grapheme. Ogni singola emoji viene conteggiata come 1. La countproprietà è anche direttamente disponibile per la stringa. Quindi puoi chiamarlo direttamente così.

"πŸ‘πŸ½".count  // 1. Not available on swift 3
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".count  // 1. Not available on swift 3

L'array di caratteri di una stringa viene anche conteggiato come cluster grapheme in Swift 4.0, quindi entrambi i seguenti codici stampano 1. Queste due emoji sono esempi di sequenze di emoji, in cui diverse emoji sono combinate insieme o senza un join a larghezza zero \u{200d}tra di loro. In swift 3.0, la matrice di caratteri di tale stringa separa ciascuna emoji e genera una matrice con piΓΉ elementi (emoji). Il joiner viene ignorato in questo processo. Tuttavia, in Swift 4.0, l'array di personaggi vede tutte le emoji come un unico pezzo. Quindi quello di qualsiasi emoji sarΓ  sempre 1.

"πŸ‘πŸ½".characters.count  // 1. In swift 3, this prints 2
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".characters.count  // 1. In swift 3, this prints 4

unicodeScalars rimane invariato in Swift 4. Fornisce i caratteri Unicode univoci nella stringa specificata.

"πŸ‘πŸ½".unicodeScalars.count  // 2. Combination of two emoji
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".unicodeScalars.count  // 7. Combination of four emoji with joiner between them

2. Contiene

In Swift 4.0, il containsmetodo ignora il joiner a larghezza zero nelle emoji. Quindi restituisce true per uno dei quattro componenti emoji di "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦"e restituisce false se si controlla il joiner. Tuttavia, in Swift 3.0, il joiner non viene ignorato e viene combinato con le emoji che lo precedono. Quindi, quando controlli se "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦"contiene le prime tre emoji dei componenti, il risultato sarΓ  falso

"πŸ‘πŸ½".contains("πŸ‘")       // true
"πŸ‘πŸ½".contains("🏽")        // true
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦")       // true
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘©")       // true. In swift 3, this prints false
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("\u{200D}") // false
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘§")       // true. In swift 3, this prints false
"πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦".contains("πŸ‘¦")       // true

0

Gli emoji, proprio come lo standard unicode, sono ingannevolmente complicati. TonalitΓ  della pelle, generi, lavori, gruppi di persone, sequenze di falegnami a larghezza zero, bandiere (unicode a 2 caratteri) e altre complicazioni possono rendere disordinato l'analisi delle emoji. Un albero di Natale, una fetta di pizza o una pila di cacca puΓ² essere rappresentato con un singolo punto di codice Unicode. Per non parlare del fatto che quando vengono introdotti nuovi emoji, c'Γ¨ un ritardo tra il supporto iOS e il rilascio di emoji. Questo e il fatto che versioni diverse di iOS supportano versioni diverse dello standard unicode.

TL; DR. Ho lavorato su queste funzionalitΓ  e ho aperto una libreria che sono l'autore di JKEmoji per aiutare a analizzare le stringhe con emoji. Rende l'analisi semplice come:

print("I love these emojis πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸ’ͺ🏾πŸ§₯πŸ‘§πŸΏπŸŒˆ".emojiCount)

5

Lo fa aggiornando di routine un database locale di tutti gli emoji riconosciuti a partire dall'ultima versione Unicode ( 12.0 di recente) e facendo un riferimento incrociato a ciΓ² che viene riconosciuto come emoji valido nella versione del sistema operativo in esecuzione guardando la rappresentazione bitmap di un personaggio emoji non riconosciuto.

NOTA

Una precedente risposta Γ¨ stata cancellata per pubblicizzare la mia biblioteca senza dichiarare chiaramente che io sono l'autore. Lo riconosco di nuovo.


2
Mentre sono impressionato dalla tua biblioteca e vedo come Γ¨ generalmente correlato all'argomento in questione, non vedo come questo sia direttamente correlato alla domanda
Ben Leggiero
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.