Por POSIX, os intervalos nas expressões de colchetes são especificados apenas para serem baseados no ponto de código no código do idioma C / POSIX. Em outras localidades, não é especificado e geralmente é baseado na ordem de agrupamento conforme você descobriu. Você verá que, em alguns locais, dependendo da ferramenta, [g-j]
, por exemplo, inclui i
, mas também ı
, ǵ
, às vezes até I
ou mesmo ch
, como em algumas localidades tchecas. / p>
zsh
é um desses raros cujos intervalos [x-y]
são baseados no ponto de código, independentemente da localidade. Para conjuntos de caracteres de byte único, isso será baseado no valor de byte, para os de múltiplos bytes no ponto de código Unicode ou qualquer que seja o sistema usado para representar caracteres largos internamente com mbstowc()
e co. APIs (geralmente Unicode).
Então, em zsh
,
-
[[ $char = [$'\u452'-$'\u490'] ]]
-
[[ $char = [^ђ-Ґ] ]]
-
y=${x//[^ђ-Ґ]/}
funcionaria no seu caso para corresponder aos caracteres nesse intervalo de Unicode, desde que o conjunto de caracteres do código de idioma seja multi-byte e tenha esses dois caracteres. Existem conjuntos de caracteres de byte único que contêm alguns desses caracteres (como ISO8859-5 que tem a maioria dos que estão em U + 0401 ... U + 045F), mas em locais que os usam, os intervalos [ђ-Ґ]
seriam baseados em o valor do byte (ponto de código no conjunto de caracteres correspondente, não o ponto de código Unicode).
Na localidade C, os intervalos são baseados no ponto de código, mas o conjunto de caracteres no idioma C só é garantido para incluir os caracteres no conjunto de caracteres portátil que é apenas os poucos caracteres necessários para gravar Código POSIX ou C (nenhum dos quais está no script cirílico). Também é garantido que seja byte único , portanto, não é possível incluir todos os caracteres especificados em Unicode. Na prática, é mais frequentemente ASCII.
Na prática, você não pode definir LC_COLLATE
para C sem definir também LC_CTYPE
para C (ou pelo menos um código de idioma com um conjunto de caracteres de um único byte). No entanto, muitos sistemas têm um C.UTF-8
locale que você pode usar aqui.
O UTF-8 é um desses conjuntos de caracteres que podem representar todos os caracteres Unicode e, portanto, todos aqueles em qualquer conjunto de caracteres. Então você poderia fazer:
< file iconv -t utf-8 |
LC_ALL=C.UTF-8 sh -c 'sed "$(printf "s/[^12-20]//g")"' |
iconv -f utf-8
A primeira iconv
convertendo do charset de localidade do usuário para UTF-8, 12
e 20
sendo a codificação UTF-8 de U + 0452 e U + 0490 respectivamente, o segundo iconv
convertendo de volta para o charset do locale.
Se a localidade atual já usa UTF-8 como o conjunto de caracteres (e file
é escrito usando esse conjunto de caracteres), isso pode ser simplificado para:
<file LC_ALL=C.UTF-8 sed 's/[^ђ-Ґ]//g'
ou:
<file LC_ALL=C.UTF-8 sed "$(printf "s/[^12-20]//g")"
Com o GNU sed
e desde que o $POSIXLY_CORRECT
não esteja no ambiente, você pode especificar caracteres com base no valor de bytes de sua codificação.
<file LC_ALL=C.UTF-8 sed 's/[^12-20]//g'
Embora em versões mais antigas, você pode precisar de:
<file LC_ALL=C.UTF-8 sed 's/[^\o321\o222-\o322\o220]//g'
Ou a variante hexadecimal:
<file LC_ALL=C.UTF-8 sed 's/[^\xd1\x92-\xd2\x90]//g'
Outra opção, para locais usando um conjunto de caracteres de múltiplos bytes que inclui esses caracteres em sistemas nos quais a representação de caracteres largos é baseada em Unicode, é usar o GNU awk
e:
awk 'BEGIN{for (i = 0x452; i<=0x490; i++) range = range sprintf("%c", i)}
{gsub("[^" range "]", ""); print}'
(Inicialmente, eu acreditava que POSIX requeria que as implementações do awk se comportassem como o GNU awk, mas esse não é o caso, já que o POSIX deixa o comportamento de sprintf("%c", i)
undefined para valores de i
que não correspondem ao codificação (não codepoint) de um caractere na localidade, o que significa que ele não pode ser usado portualmente para caracteres de múltiplos bytes).
Em qualquer caso, observe que o intervalo U + 0400 .. U + 052F não são os únicos caracteres Unicode no script cirílico , e muito menos os idiomas que usam Cirílico como seu script. A lista de caracteres também varia com a versão do Unicode.
Em um sistema semelhante ao Debian, você pode obter uma lista deles com:
unicode --max 0 cyrillic
(que dá 435 diferentes no Ubuntu 16.04, 444 no Debian sid (provavelmente usando uma versão diferente do Unicode).
Em perl
, consulte \p{Block: Cyrillic}
, \p{Block: Cyrillic_Ext_A,B,C}
, \p{Block: Cyrillic_Supplement}
... para corresponder nos blocos Unicode e \p{Cyrillic}
para corresponder aos caracteres do script cirílico (atualmente atribuído na versão Unicode que sua versão de perl
está usando (veja perl -MUnicode::UCD -le 'print Unicode::UCD::UnicodeVersion'
, por exemplo)).
Então:
perl -Mopen=locale 's/\P{Cyrillic}//g'