El carácter 👩👩👧👦 (familia con dos mujeres, una niña y un niño) está codificado como tal:
U+1F469
WOMAN
`
U+200D
ZWJ
`
U+1F469
WOMAN
`
U+200D
ZWJ
`
U+1F467
GIRL
`
U+200D
ZWJ
`
U+1F466
BOY
Entonces está muy interesantemente codificado; El blanco perfecto para una prueba unitaria. Sin embargo, Swift no parece saber cómo tratarlo. Esto es lo que quiero decir:
"👩👩👧👦".contains("👩👩👧👦") // true
"👩👩👧👦".contains("👩") // false
"👩👩👧👦".contains("\u{200D}") // false
"👩👩👧👦".contains("👧") // false
"👩👩👧👦".contains("👦") // true
Entonces, Swift dice que se contiene a sí mismo (bueno) y a un niño (¡bueno!). Pero luego dice que no contiene una mujer, una niña o un carpintero de ancho cero. ¿Que esta pasando aqui? ¿Por qué Swift sabe que contiene un niño pero no una mujer o una niña? Podía entender si lo trataba como un solo personaje y solo reconocía que se contenía a sí mismo, pero el hecho de que tuviera un subcomponente y ningún otro me desconcierta.
Esto no cambia si uso algo como "👩".characters.first!
.
Aún más confuso es esto:
let manual = "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}"
Array(manual.characters) // ["👩", "👩", "👧", "👦"]
Aunque coloqué los ZWJ allí, no se reflejan en la matriz de caracteres. Lo que siguió fue un poco revelador:
manual.contains("👩") // false
manual.contains("👧") // false
manual.contains("👦") // true
Entonces obtengo el mismo comportamiento con la matriz de caracteres ... lo cual es sumamente molesto, ya que sé cómo se ve la matriz.
Esto tampoco cambia si uso algo como "👩".characters.first!
.
"👩👩👧👦".contains("\u{200D}")
corrigió en Swift 4. todavía devuelve falso, no estoy seguro de si eso es un error o característicaRespuestas:
Esto tiene que ver con cómo funciona el
String
tipo en Swift y cómo funciona elcontains(_:)
método.La '👩👩👧👦' es lo que se conoce como una secuencia de emoji, que se representa como un carácter visible en una cadena. La secuencia está compuesta de
Character
objetos, y al mismo tiempo está compuesta deUnicodeScalar
objetos.Si verifica el recuento de caracteres de la cadena, verá que está formado por cuatro caracteres, mientras que si verifica el recuento escalar unicode, le mostrará un resultado diferente:
Ahora, si analiza los caracteres e los imprime, verá lo que parecen caracteres normales, pero de hecho, los tres primeros caracteres contienen tanto un emoji como un carpintero de ancho cero en su
UnicodeScalarView
:Como puede ver, solo el último carácter no contiene una unión de ancho cero, por lo que cuando usa el
contains(_:)
método, funciona como es de esperar. Como no se compara con los emoji que contienen uniones de ancho cero, el método no encontrará una coincidencia para ningún otro personaje que no sea el último.Para ampliar esto, si crea uno
String
que está compuesto por un carácter emoji que termina con una unión de ancho cero y lo pasa alcontains(_:)
método, también se evaluaráfalse
. Esto tiene que ver concontains(_:)
ser exactamente igual querange(of:) != nil
, que trata de encontrar una coincidencia exacta con el argumento dado. Dado que los caracteres que terminan con una unión de ancho cero forman una secuencia incompleta, el método intenta encontrar una coincidencia para el argumento mientras combina caracteres que terminan con uniones de ancho cero en una secuencia completa. Esto significa que el método nunca encontrará una coincidencia si:Demostrar:
Sin embargo, dado que la comparación solo mira hacia adelante, puede encontrar varias otras secuencias completas dentro de la cadena trabajando hacia atrás:
La solución más fácil sería proporcionar una opción de comparación específica para el
range(of:options:range:locale:)
método. La opciónString.CompareOptions.literal
realiza la comparación en una equivalencia exacta de carácter por carácter . Como nota al margen, lo que se entiende por carácter aquí no es SwiftCharacter
, sino la representación UTF-16 de la cadena de instancia y de comparación; sin embargo, dadoString
que no permite UTF-16 con formato incorrecto, esto es esencialmente equivalente a comparar el escalar Unicode representación.Aquí he sobrecargado el
Foundation
método, así que si necesitas el original, renombra este o algo así:Ahora el método funciona como "debería" con cada personaje, incluso con secuencias incompletas:
fuente
"👩👩👧👦".count
evalúa1
con la versión actual de Xcode 9 beta y Swift 4.El primer problema es que estás conectando a Foundation con
contains
(SwiftString
no es unCollection
), así que este es elNSString
comportamiento, que no creo que maneje Emoji compuesto tan poderosamente como Swift. Dicho esto, Swift, creo, está implementando Unicode 8 en este momento, que también necesitaba revisión en torno a esta situación en Unicode 10 (por lo que todo esto puede cambiar cuando implementan Unicode 10; no he investigado si lo hará o no).Para simplificar, eliminemos Foundation y usemos Swift, que proporciona vistas más explícitas. Comenzaremos con los personajes:
OKAY. Eso es lo que esperábamos. Pero es mentira. Veamos cuáles son esos personajes realmente.
Ah ... así es
["👩ZWJ", "👩ZWJ", "👧ZWJ", "👦"]
. Eso deja todo un poco más claro. 👩 no es miembro de esta lista (es "👩ZWJ"), pero 👦 es miembro.El problema es que
Character
es un "grupo de grafemas", que compone cosas juntas (como adjuntar el ZWJ). Lo que realmente estás buscando es un escalar unicode. Y eso funciona exactamente como esperabas:Y, por supuesto, también podemos buscar el personaje real que está allí:
(Esto duplica en gran medida los puntos de Ben Leggiero. Publiqué esto antes de notar que había respondido. Partir en caso de que sea más claro para alguien).
fuente
ZWJ
Qué significa?String
supuestamente se cambió de nuevo a un tipo de colección. ¿Eso afecta tu respuesta?Parece que Swift considera que
ZWJ
a es un grupo de grafemas extendido con el personaje inmediatamente anterior. Podemos ver esto cuando asignamos la matriz de caracteres a susunicodeScalars
:Esto imprime lo siguiente de LLDB:
Además, los
.contains
grupos extendieron los grupos de grafemas en un solo personaje. Por ejemplo, tomando los caracteres de Hangulᄒ
,ᅡ
yᆫ
(que se combinan para hacer que la palabra coreana para "uno":한
):Esto no se pudo encontrar
ᄒ
porque los tres puntos de código se agrupan en un clúster que actúa como un solo carácter. Del mismo modo,\u{1F469}\u{200D}
(WOMAN
ZWJ
) es un grupo, que actúa como un carácter.fuente
Las otras respuestas discuten lo que hace Swift, pero no entran en muchos detalles sobre por qué.
¿Espera que "Å" sea igual a "Å"? Espero que lo hagas.
Una de ellas es una letra con un combinador, la otra es un único personaje compuesto. Puedes agregar muchos combinadores diferentes a un personaje base, y un humano aún lo consideraría como un personaje único. Para lidiar con este tipo de discrepancia, se creó el concepto de grafema para representar lo que un humano consideraría un personaje, independientemente de los puntos de código utilizados.
Ahora los servicios de mensajes de texto han estado combinando caracteres en emoji gráficos durante años
:)
→🙂
. Así que se agregaron varios emoji a Unicode.Estos servicios también comenzaron a combinar emoji en emoji compuesto.
Por supuesto, no hay una forma razonable de codificar todas las combinaciones posibles en puntos de código individuales, por lo que El Consorcio Unicode decidió ampliar el concepto de grafemas para abarcar estos caracteres compuestos.
Lo que se reduce a esto se
"👩👩👧👦"
debe considerar como un solo "grupo de grafemas" si intenta trabajar con él a nivel de grafema, como lo hace Swift de manera predeterminada.Si quieres comprobar si contiene
"👦"
como parte de eso, entonces debe bajar a un nivel inferior.No conozco la sintaxis de Swift, así que aquí hay un Perl 6 que tiene un nivel similar de soporte para Unicode.
(Perl 6 admite la versión 9 de Unicode, por lo que puede haber discrepancias)
Bajemos un nivel
Sin embargo, bajar a este nivel puede hacer que algunas cosas sean más difíciles.
yo asumo eso
.contains
en Swift lo hace más fácil, pero eso no significa que no haya otras cosas que se vuelvan más difíciles.Trabajar en este nivel hace que sea mucho más fácil dividir accidentalmente una cadena en el medio de un carácter compuesto, por ejemplo.
Lo que inadvertidamente pregunta es por qué esta representación de nivel superior no funciona como lo haría una representación de nivel inferior. La respuesta es, por supuesto, no se supone que lo haga.
Si se pregunta " por qué esto tiene que ser tan complicado ", la respuesta es, por supuesto, " humanos ".
fuente
rotor
y quegrep
hacer aqui Y lo que es1-$l
?rotor
. El códigosay (1,2,3,4,5,6).rotor(3)
cede((1 2 3) (4 5 6))
. Esa es una lista de listas, cada longitud3
.say (1,2,3,4,5,6).rotor(3=>-2)
produce lo mismo, excepto que la segunda sublista comienza con, en2
lugar de4
, la tercera con3
, y así sucesivamente, cediendo((1 2 3) (2 3 4) (3 4 5) (4 5 6))
. Si@match
contiene,"👩👩👧👦".ords
entonces el código de @ Brad crea solo una sublista, por lo que el=>1-$l
bit es irrelevante (no utilizado). Solo es relevante si@match
es más corto que@components
.grep
intenta hacer coincidir cada elemento en su invocante (en este caso, una lista de sublistas de@components
). Intenta hacer coincidir cada elemento con su argumento de coincidencia (en este caso,@match
). Los.Bool
rendimientos luegoTrue
FIB lagrep
produce al menos un partido.Actualización de Swift 4.0
String recibió muchas revisiones en la actualización de Swift 4, como se documenta en SE-0163 . Se utilizan dos emoji para esta demostración que representan dos estructuras diferentes. Ambos se combinan con una secuencia de emoji.
👍🏽
es la combinación de dos emoji👍
y🏽
👩👩👧👦
es la combinación de cuatro emoji, con carpintero de ancho cero conectado. El formato es👩joiner👩joiner👧joiner👦
1. Cuenta
En Swift 4.0, los emoji se cuentan como un grupo de grafemas. Cada emoji se cuenta como 1. La
count
propiedad también está disponible directamente para la cadena. Entonces puedes llamarlo directamente así.La matriz de caracteres de una cadena también se cuenta como grupos de grafemas en Swift 4.0, por lo que se imprimen los dos códigos siguientes 1. Estos dos emoji son ejemplos de secuencias de emoji, donde varios emoji se combinan con o sin unión de ancho cero
\u{200d}
entre ellos. En swift 3.0, la matriz de caracteres de dicha cadena separa cada emoji y da como resultado una matriz con múltiples elementos (emoji). El carpintero se ignora en este proceso. Sin embargo, en Swift 4.0, la matriz de caracteres ve todos los emoji como una sola pieza. Entonces, el de cualquier emoji siempre será 1.unicodeScalars
permanece sin cambios en Swift 4. Proporciona los caracteres únicos Unicode en la cadena dada.2. Contiene
En Swift 4.0, el
contains
método ignora la unión de ancho cero en emoji. Por lo tanto, devuelve verdadero para cualquiera de los cuatro componentes emoji de"👩👩👧👦"
, y devuelve falso si verifica la unión. Sin embargo, en Swift 3.0, el carpintero no se ignora y se combina con el emoji frente a él. Entonces, cuando verifica si"👩👩👧👦"
contiene los primeros tres componentes emoji, el resultado será falsofuente
Los emojis, al igual que el estándar Unicode, son engañosamente complicados. Los tonos de piel, los géneros, los trabajos, los grupos de personas, las secuencias de carpintería de ancho cero, las banderas (2 caracteres unicode) y otras complicaciones pueden hacer que el análisis de emoji sea desordenado. Un árbol de Navidad, una rebanada de pizza o una pila de caca se pueden representar con un único punto de código Unicode. Sin mencionar que cuando se introducen nuevos emojis, hay un retraso entre el soporte de iOS y el lanzamiento de emoji. Eso y el hecho de que diferentes versiones de iOS admiten diferentes versiones del estándar Unicode.
TL; DR. Trabajé en estas características y abrí una biblioteca. Soy el autor de JKEmoji para ayudar a analizar cadenas con emojis. Hace que el análisis sea tan fácil como:
Lo hace actualizando rutinariamente una base de datos local de todos los emojis reconocidos a partir de la última versión Unicode ( 12.0 a la fecha reciente) y haciendo referencias cruzadas con lo que se reconoce como un emoji válido en la versión del sistema operativo en ejecución al observar la representación de mapa de bits de Un personaje emoji no reconocido.
NOTA
Se eliminó una respuesta anterior por anunciar mi biblioteca sin indicar claramente que yo soy el autor. Estoy reconociendo esto nuevamente.
fuente