LINUX.ORG.RU

[tcl]Не могу разобратся с encoding

 


1

1

Берем скрипт:

$ cat test.tcl
set test "Привет"
puts stdout [encoding system]
set testenc1 [encoding convertto cp1251 $test]
set testenc2 [encoding convertto koi8-r $test]
puts stdout $test
puts stdout $testenc1
puts stdout $testenc2
Запускаем в терминале с кодировкой utf8 в настройках:
$ tclsh8.5 test.tcl
utf-8
Привет
Ïðèâåò
ðÒÉ×ÅÔ
Меняем в настройках терминала кодировку на cp1251:
$ tclsh8.5 test.tcl
utf-8
Привет
Ïðèâåò
ðÒÉ×ÅÔ
А теперь меняем кодировку терминала на koi8-r:
$ tclsh8.5 test.tcl
utf-8
п÷я─п╦п╡п╣я┌
ц▐ц╟ц╗ц╒ц╔ц╡
ц╟ц▓ц┴ц≈ц┘ц■

И собственно вопрос: почему encoding не правильно конвертирует строку в соответствующую кодировку? Я не думаю что дело в терминале (konsole) потому что в других случаях он с кодировками работает правильно (например, если установить кодировку на cp1251 и сделать cat на текстовый файл в этой кодировке, то русский текст отображается правильно). Или я не правильно понимаю как должен работать encoding?

★★

Ваш test.tcl кириллицу в какой кодировке содержит? На глаз - по-моему, всё нормально скрипт отрабатывает.

Xenesz ★★★★
()
Ответ на: комментарий от Xenesz

>Ваш test.tcl кириллицу в какой кодировке содержит?

Сам скрипт сохранен в utf-8

>На глаз - по-моему, всё нормально скрипт отрабатывает.


Почему-же в результате:

set testenc1 [encoding convertto cp1251 $test]
set testenc2 [encoding convertto koi8-r $test]

получаются строки по 12 байт а не по 6 (cp1251 и koi8-r, ведь вроде однобайтовые)? И, что важнее, в программе с соответствующей кодировкой эти строки не отображаются нормально.

cPunk ★★
() автор топика
Ответ на: комментарий от cPunk

внутреннее представление строк в tcl в utf-8
и кодировка cp1251 положена на строку в utf-8
по таблицам:
http://www.utf8-chartable.de/
и
http://ru.wikipedia.org/wiki/Windows-1251

видно , что буква "т" в cp1251 имеет код F2
и сохранена как 00F2 с отображаемым символом ò

elipse ★★★
()
Ответ на: комментарий от elipse

>видно , что буква "т" в cp1251 имеет код F2
>и сохранена как 00F2 с отображаемым символом ò


Понятно. Тогда другой вопрос: существует ли способ получить, например на тот же stdout, из tcl, "правильный" однобайтовый cp1251 и/или koi8-r?

cPunk ★★
() автор топика
Ответ на: комментарий от cPunk

так как stdout работает в utf-8 - только обратное преобразование в utf-8.
ps: можно попробовать использовать списки для байтовых кодировок.

elipse ★★★
()
Ответ на: комментарий от cPunk

> Почему-же в результате:
> set testenc1 [encoding convertto cp1251 $test]
> set testenc2 [encoding convertto koi8-r $test]
> получаются строки по 12 байт а не по 6 (cp1251 и koi8-r, ведь вроде однобайтовые)?

потому что в tcl начиная с 8.какой-то версии все строки в unicode

http://www.tcl.tk/man/tcl8.4/TclCmd/encoding.htm#M4

Strings in Tcl are encoded using 16-bit Unicode characters.

[encoding convertto cp1251 $test] возвращает unicode строку, в которой первый байт — это Ваш символ в кодировке cp1251, а второй байт = 0

http://www.tcl.tk/man/tcl8.4/TclCmd/encoding.htm#M7

Convert string from Unicode to the specified encoding. The result is a sequence of bytes that represents the converted string. Each byte is stored in the lower 8-bits of a Unicode character. If encoding is not specified, the current system encoding is used. 

> И, что важнее, в программе с соответствующей кодировкой эти строки не отображаются нормально.

Потому что Вы неправильно делаете. Вместо извращений с кодировками _внутри_ скрипта нужно просто правильно установить кодировку _входных и выходных данных_, что бы все необходимые преобразования кодировок делал сам tcl, автоматически.

Например, если Вы хотите выводить в stdout в кодировке cp1251 сделайте:

fconfigure stdout -encoding cp1251

Возможно этот пример поможет Вам разобраться:
proc hd {s} {
    set r {}
    foreach c [split $s {}] {
	scan $c %c n
	append r "[format %#06x $n] ([format %c $n]) "
    }
    return $r
}

set t "Привет"
set a "Hello"
set c [encoding convertto cp1251 $t]

puts "org [string bytelength $t] [hd $t]"
puts "enc [string bytelength $c] [hd $c]"
puts "asc [string bytelength $a] [hd $a]"

$ tclsh a.tcl
org 12 0x041f (П) 0x0440 (р) 0x0438 (и) 0x0432 (в) 0x0435 (е) 0x0442 (т)
enc 12 0x00cf (Ï) 0x00f0 (ð) 0x00e8 (è) 0x00e2 (â) 0x00e5 (å) 0x00f2 (ò)
asc 5 0x0048 (H) 0x0065 (e) 0x006c (l) 0x006c (l) 0x006f (o)
$ echo -n Привет | iconv -t cp1251 | hd
00000000  cf f0 e8 e2 e5 f2                                 |......|

[encoding convertto cp1251 $t] конвертирует правильно — в первые 255 символов от 0 — до 255, а у unicode на этом месте находится LATIN-1, поэтому Вы видите Ïðèâåò

Eshkin_kot ★★
()
Ответ на: комментарий от Eshkin_kot

Категорически поддерживаю ёшкинокотский ответ.

В качестве дополнения: не стоит писать в скрипте символы не из latin-1, т.к. тогда на системе с другой кодировкой локали всё поедет.

Решений два: 1. научиться использовать msgcat и вместо "привет" писать [ msgcat::mc "hello" ], а перевод класть в отдельный файл

2. в заголовке писать #!/usr/bin/wish -encoding utf-8 или #!/bin/sh # \ exec wish -encoding utf-8 Работает начиная с версии 8.5, но для не-юниксовых систем (т.е. винды) опять вылезут грабельки из пункта 1, т.к. там этот заголовок работать не будет.

gaa ★★
()
Ответ на: комментарий от gaa

Так как совсем непонятны цели и задачи автора топика - то, никаких тут категорических советов. :)))
Могу придумать условия при которых все эти советы полетят в мусорник.

elipse ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.