Este é um tema vasto! É possível escrever um livro comprido sobre ele, e várias pessoas já o fizeram (procura "unicode book" na Amazon para ver alguns exemplos).
Trabalhar com carateres nos computadores era muito mais simples nas décadas anteriores, quando os programadores assumiam que o inglês era a única língua importante. Então: 26 letras, maiúsculas e minúsculas, 10 algarismos, vários sinais de pontuação, mais um código (0x07) para tocar uma campainha, e tudo cabia em 7 bits: o conjunto de carateres ASCII.
Naturalmente, as pessoas começaram a perguntar o que se passava com à, ä e Ł, depois outras pessoas começaram a perguntar sobre ऄ, ஹ e ญ, e as pessoas mais jovens queriam emojis 😱. O que fazer?
Para encurtar a história, muitas pessoas inteligentes e pacientes tiveram de integrar comités durante anos, a definir os detalhes do conjunto de carateres Unicode e de codificações como o UTF-8, e muito software precisou de uma reescrita muito complicada. Além disso, foram introduzidos muitos bugs novos.
Para evitar que tudo se avarie, a forma como o Unicode/UTF-8 foi concebido garante que os primeiros 127 códigos são idênticos ao ASCII (mesmo a campainha).
As linguagens concebidas depois de 2005 têm a enorme vantagem de já existir um padrão Unicode razoavelmente estável.
Julia (lançada pela primeira vez em 2012) pôde assumir que tudo seria Unicode: carateres, strings, nomes de variáveis e de funções, operadores matemáticos...
Como diz o manual: "A Julia torna simples e eficiente lidar com texto ASCII comum, e tratar Unicode é tão simples e eficiente quanto possível." Repara no "quanto possível": é uma parte importante da afirmação.
Os literais de caráter escrevem-se entre aspas simples, e são distintos das strings escritas entre aspas duplas.
Isto é óbvio para quem vem do mundo C/C++, mas pode ser confuso para programadores de Python e JavaScript.
julia> a = 'a' # Roman alphabet
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
julia> typeof(a)
Char
julia> jha = 'झ' # Devanagari alphabet
'झ': Unicode U+091D (category Lo: Letter, other)
julia> typeof(jha)
Char
julia> '❤' # heart emoji
'❤': Unicode U+2764 (category So: Symbol, other)
Vemos pelos exemplos que o tipo é Char, e que a Julia tem mais informação sobre a categoria do caráter.
Olhando com mais atenção, estes carateres podem ser representados por 4 algarismos hexadecimais. O conjunto completo de carateres precisa de até 6 algarismos hexadecimais.
Estes números chamam-se "pontos de código" e vão atualmente de U+0000 a U+10FFFF.
Aparecem no REPL, mas dentro do código usa codepoints() para os obter.
Converter entre Char e Int é simples:
julia> Int('a')
97
julia> Char(97)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
O compilador permite algumas formas de aritmética de inteiros com Chars:
julia> 'b' - 'a' # interval, in alphabetic order
1
julia> 'b' + 'a'
ERROR: MethodError: no method matching +(::Char, ::Char)
julia> 'a' + 5
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)
julia> 'f' + ('A' - 'a') # same as `uppercase('f')`
'F': ASCII/Unicode U+0046 (category Lu: Letter, uppercase)
Muitas funções que manipulam strings também funcionam com um Char.
uppercase() e lowercase().isuppercase() e islowercase().isletter(), abrange muitos alfabetosisdigit(), testa estritamente 0:9isnumeric(), mais abrangente do que isdigit, pelo que dá true para ¾ e para várias escritas não europeiasisxdigit(), algarismos hexadecimaisisascii(), caráter anterior ao Unicodeispunct(), pontuaçãoisspace(), qualquer caráter de espaço em brancoisprint(), carateres imprimíveis (o oposto é iscntrl())islowercase('A') # false
uppercase('γ') # 'Γ': Unicode U+0393 (category Lu: Letter, uppercase)
ispunct('@') # true
isdigit('A') # false
isxdigit('A') # true
Para converter uma string num vetor de Char, podemos usar collect().
Para converter um vetor de Char numa string, existe o construtor String().
julia> s = "abcde"
"abcde"
julia> cv = collect(s)
5-element Vector{Char}:
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase)
'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase)
julia> String(cv)
"abcde"
Isto funciona com quaisquer carateres, não apenas com ASCII.
julia> collect("❤,😱")
3-element Vector{Char}:
'❤': Unicode U+2764 (category So: Symbol, other)
',': ASCII/Unicode U+002C (category Po: Punctuation, other)
'😱': Unicode U+1F631 (category So: Symbol, other)
Repara que o construtor String() opera sobre um Vector.
Para converter um único Char numa string de 1 caráter, a função é string(), com s minúsculo.
julia> string('a')
"a"
Tudo o que vimos até aqui no documento parece relativamente simples. Então será que não há nada com que nos preocuparmos?
Infelizmente, isso é demasiado otimista!
Uma complicação vem da necessidade de "até" 6 algarismos hexadecimais por ponto de código. Isto significa que carateres diferentes precisam de quantidades diferentes de espaço em memória quando codificados em UTF-8.
Um byte só consegue armazenar números (sem sinal) até 255, dois algarismos hexadecimais, pelo que o UTF-8 usa um número variável de bytes (1 a 4) para armazenar um Char.
Estes chamam-se "unidades de código", e a função ncodeunits() devolve o número necessário para um determinado caráter.
julia> codepoint(jha) # jha 'झ' is defined in an earlier example
0x0000091d
julia> ncodeunits(jha)
3
julia> ncodeunits('a') # ASCII character
1
julia> ncodeunits('😱') # emoji
4
Além disso, nem tudo o que pode ser apresentado no ecrã tem o seu próprio ponto de código único. Alguns carateres visualmente distintos são considerados derivados de outros, pelo que o Unicode os trata como um caráter base mais um modificador.
Este problema afeta as strings, onde coloca desafios à indexação.
Neste exercício vais implementar um conjunto parcial de rotinas utilitárias para ajudar um programador a limpar nomes de identificadores.
Ao longo das 6 tarefas vais construir gradualmente as funções transform, para converter carateres individuais, e clean, para converter strings.
Um identificador válido é composto por zero ou mais letras, sublinhados, hífenes, pontos de interrogação e emojis.
Se for passada uma string vazia à função clean, deve ser devolvida uma string vazia.
Implementa a função transform para substituir os hífenes por sublinhados.
julia> transform('-')
"_"
Remove todos os carateres de espaço em branco. Isto inclui espaços em branco no início e no fim.
julia> transform(' ')
""
Modifica a função transform para converter camelCase em kebab-case
julia> transform('D')
"-d"
Modifica a função transform para omitir os carateres que sejam numéricos.
julia> transform('7')
""
Modifica a função transform para substituir as letras gregas no intervalo de 'α' a 'ω'.
julia> transform('β')
"?"
Implementa a função clean para aplicar estas operações a uma string inteira.
Os carateres que não se enquadram nas regras devem passar inalterados.
julia> clean(" a2b Cd-ω😀 ")
"ab-cd_?😀"
Inscreve-te no Exercism para aprenderes e dominares Julia com 35 conceitos128 exercícios, e mentoria humana real, tudo grátis.