View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0000752 | Far Manager | Viewer | public | 2009-02-23 10:58 | 2011-03-21 23:19 |
Reporter | theultramage | Assigned To | alexy | ||
Priority | normal | Severity | minor | Reproducibility | have not tried |
Status | closed | Resolution | fixed | ||
Fixed in Version | 3.0 | ||||
Summary | 0000752: hex view on utf8 files shows each byte as 0x00 | ||||
Description | When viewing an utf8 file, and switching to hex view (F4), all data shows as 00. Is this the best way to handle this sort of situation? Example: 0000000000: 00 00 00 00 00 00 │ ?áéíóú When using shift+F8 to change codepage to ANSI, it displays corectly. When using F8 to switch between ANSI/OEM, it switches to ascii display and works too. | ||||
Additional Information | To reproduce: 1. create new file with encoding set to UTF-8 2. write something and save 3. view the new file in hex mode (F3, then F4) | ||||
Tags | No tags attached. | ||||
Build | 1925 | ||||
|
Перенесено из 0000854 > В общем тут есть такое (цитата): > "А зачем в Viewer::ShowHex() вызывается вот это: > WideCharToMultiByte(VM.CodePage, 0, (const wchar_t*)&Ch,1, &NewCh,1," ",NULL); > после чего печатается 16-ричный код NewCh (который для файлв в utf8 > может принимать любое значение, зависящее от положения луны, погодных > условий и пр.)" > >возможно это и лечение. >"возможно" - потому, что пока не пойму на что оно может повлиять. > > alexmitin: закомменть эту строку и попытайся "исследовать" > на что оно повлияет :-) Там проблема глубже. Текущий код viewer расчитан на представление текста в разных кодировках. Но он не очень хорошо расчитан на представление hex вида для кодировок с переменным числом байт/символ. Сейчас код viewer читает данные из файла, исходя из кодировки заданной пользователем, и конвертирует их в Unicode (UTF-16) - смотри функцию Viewer::vread() - для дальнейшего показа на консоли. Для показа просто текста этого вполне достаточно. При переключении в hex режим - код в Viewer::ShowHex() пытается конвертировать данные обратно из Unicode в заданную кодировку, и только потом показать байт в виде hex значения. Тут несколько проблем: 1) Двойное преобразование может привести к потере информации. 2) Не проверяется результат функции WideCharToMultiByte. В случае неудачи значение NewCh не изменяется, а так как это значение и не проинициализировано - то оно содержит просто мусор. 3) Код не расчитан, что один символ может занимать разное число байт - как раз случай UTF-8. Оригинальная спецификация допускала от 1 до 6 байт на символ (Смотри http://en.wikipedia.org/wiki/UTF-8 ) Позже RFC3629 ограничил допустимый диапазон: каждый символ может занимать от 1 до 4 байт. Проблема хорошо видна на значениях offset в левой части. Каждая hex строка показываает 16 байт. А вот offset в случае UTF-8 кодировки не соответствует количеству байт. Скорее он показывает количество символов. Но тогда теряется смысл hex режима для UTF-8. ----------------- Для hex вида нужно читать байты из файла без изменений, показывать оригинальные значения каждого байта, а в правой части показывать текст в выбранной кодировке. Тут есть проблема, что вывод нужно форматировать с учетом позиции символа и его размера в байтах. Вполне может оказаться, что байты символа могут оказаться на разных строках hex вывода. Думаю правильнее показывать символ на позиции первого байта, а остальные места заполнять пробелами или предопределенным символом (это можно сделать конфигурируемым через регистр). Все это требует значительного изменения кода viewer. |
|
viewer.diff (6,101 bytes)
Index: viewer.cpp =================================================================== --- viewer.cpp (revision 5864) +++ viewer.cpp (working copy) @@ -74,6 +74,15 @@ static int ViewerID=0; +static int utf8_to_WideChar(const char *s, int nc, wchar_t *w1,wchar_t *w2, int wlen, int &tail); +// +//#define REPLACE_CHAR 0xFEFF // Zero Width No-Break Space +#define REPLACE_CHAR 0xFFFD // Replacement +// +//#define CONTINUE_CHAR 0x00BB // Right-Pointing Double Angle Quotation Mark +#define CONTINUE_CHAR 0x203A // Single Right-Pointing Angle Quotation Mark +//#define CONTINUE_CHAR 0x2026 // Horizontal Ellipse + Viewer::Viewer(bool bQuickView, UINT aCodePage): ViOpt(Opt.ViOpt), Reader(ViewFile), @@ -350,13 +359,6 @@ CodePageChangedByUser=TRUE; } - // BUGBUG - // ���� ��� �������� �������� hex � UTF8, ��� �� ��������. - if (VM.Hex && VM.CodePage==CP_UTF8) - { - VM.CodePage=Opt.ViOpt.AnsiCodePageAsDefault?GetACP():GetOEMCP(); - } - if (!IsUnicodeOrUtfCodePage(VM.CodePage)) { ViewFile.SetPointer(0, nullptr, FILE_BEGIN); @@ -710,6 +712,74 @@ wcscat(OutStr, BorderLine); } } + + else if ( CP_UTF8 == VM.CodePage ) + { + __int64 fpos = vtell(); + unsigned char line[16+3]; + DWORD nr = 0; + Reader.Read(line, (DWORD)sizeof(line), &nr); + if (nr > 16) + Reader.Unread(nr-16); + + if (!nr) + wcscpy(OutStr,L""); + else + { + int out_len = (int)wcslen(OutStr); + int border_len = (int)wcslen(BorderLine); + if (SelectSize) + { + if (SelectPos >= fpos && SelectPos < fpos+16) + { + int off = (int)(SelectPos - fpos); + bSelStartFound = true; + SelStart = out_len + 3*off + (off < 8 ? 0 : border_len); + SelSize = SelectSize; + } + __int64 selectEnd = SelectPos + SelectSize - 1; + if (selectEnd >= fpos && selectEnd < fpos+16) + { + int off = (int)(selectEnd - fpos); + bSelEndFound = true; + SelEnd = out_len + 3*off + (off < 8 ? 0 : border_len) + 1; + SelSize = SelectSize; + } + } + for (X=0; X<16; X++) + { + int off = out_len + 3*X + (X < 8 ? 0 : border_len); + if (X == 8) + wcscpy(OutStr+off-border_len, BorderLine); + if (X < (int)nr) + _snwprintf(OutStr+off, ARRAYSIZE(OutStr)-off, L"%02X ", (int)line[X]); + else + wcscpy(OutStr+off, L" "); + TextStr[X] = L' '; + } + + wchar_t w1[16], w2[16]; + int tail, nw, ib=0, iw=0; + nw = utf8_to_WideChar((char *)line, (int)nr, w1, w2, 16, tail); + bool first = true; + while (ib < 16 && iw < nw) + { + if (first && w1[iw] == REPLACE_CHAR && w2[iw] == L'?') + TextStr[ib++] = CONTINUE_CHAR; // ��� ����� �� �� ������ ��������� �� '����' utf-8 + else // �� ������� ��-�� ����� ��� �� ��� ���� �� �����... + { + first = false; + TextStr[ib++] = w1[iw] ? w1[iw] : L' '; + } + int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, NULL,0, NULL,NULL); + while (--clen > 0 && ib < 16) + TextStr[ib++] = CONTINUE_CHAR; + ++iw; + } + TextPos = 16; + } + } + else { for (X=0; X<16; X++) @@ -782,14 +852,7 @@ wcscat(OutStr,L" "); wcscat(OutStr,TextStr); -#if 0 - for (size_t I=0; I < wcslen(OutStr); ++I) - if (OutStr[I] == (wchar_t)0xFFFF) - OutStr[I]=L'?'; - -#endif - if (StrLength(OutStr)>HexLeftPos) { FS<<fmt::LeftAlign()<<fmt::Width(ObjWidth)<<fmt::Precision(ObjWidth)<<OutStr+static_cast<size_t>(HexLeftPos); @@ -1393,13 +1456,6 @@ { UINT nCodePage = SelectCodePage(VM.CodePage, true, true); - // BUGBUG - // ���� ��� �������� �������� hex � UTF8, ��� �� ��������. - if (VM.Hex && nCodePage==CP_UTF8) - { - return TRUE; - } - if (nCodePage!=(UINT)-1) { CodePageChangedByUser=TRUE; @@ -2726,8 +2782,6 @@ static int utf8_to_WideChar(const char *s, int nc, wchar_t *w1,wchar_t *w2, int wlen, int &tail ) { -#define REPLACEMENT 0xFFFD // L'?' - if ( wlen <= 0 ) { tail = nc; @@ -2754,7 +2808,7 @@ else { tail = 0; - w1[0] = REPLACEMENT; + w1[0] = REPLACE_CHAR; if (w2) w2[0] = L'?'; nw = 1; @@ -2762,7 +2816,7 @@ return nw; } c2 = ((const unsigned char *)s)[ic]; - if ( 0x80 != (c2 & 0xC0) // illegal 2-st byte + if ( 0x80 != (c2 & 0xC0) // illegal 2-nd byte || (0xE0 == c1 && c2 <= 0x9F) // illegal 3-byte start (overlaps with 2-byte) || (0xF0 == c1 && c2 <= 0x8F) // illegal 4-byte start (overlaps with 3-byte) || (0xF4 == c1 && c2 >= 0x90) // illegal 4-byte (out of unicode range) @@ -2811,7 +2865,7 @@ if (nw >= wlen) { --nw; - wc = REPLACEMENT; + wc = REPLACE_CHAR; } } } @@ -2826,7 +2880,7 @@ } else { - w1[nw] = REPLACEMENT; + w1[nw] = REPLACE_CHAR; if (w2) w2[nw] = L'?'; } @@ -3482,14 +3536,6 @@ int Viewer::ProcessHexMode(int newMode, bool isRedraw) { - // BUGBUG - // �� �� ���, ���� �� ����� ���������� ��������� hex-�������� � UTF8 - ����� ������� � OEM. - // ��� ����� ��� �� ��������, � ��������� �������� ��� �����������. - if (VM.CodePage==CP_UTF8 && newMode) - { - VM.CodePage=Opt.ViOpt.AnsiCodePageAsDefault?GetACP():GetOEMCP(); - } - int oldHex=VM.Hex; VM.Hex=newMode&1; |
|
|
|
предлагается вариант hex-view для utf-8 |
|
вроде не плохо. Тока может придумать что то чтоб этот "continue char" как то цветом помечать? З.Ы. Почем wcscpy(OutStr,L""); а не *OutStr=0;? |
|
копипаст из кода для однобайтных кодировок (ниже)... И тогда уж... = L'\0'; насчет цвета - никогда не делал. если кто-нибудь сделает, у меня никаких возражений нет :) |
|
1925 |
|
заметил одну фичу: в far2 при переключении по F4 выделение найденного сохраняется, а в far3 теряется. Наверное правильнее было-бы сохранить поведение far2. Или в far3 'правильное' поведение? |
|
Fixed in 1925. А про выделение это уже для другого тикета тема. |
Date Modified | Username | Field | Change |
---|---|---|---|
2009-02-23 10:58 | theultramage | New Issue | |
2009-04-15 08:30 | alexy | Relationship added | has duplicate 0000826 |
2009-05-05 10:11 | vskirdin | Relationship added | has duplicate 0000854 |
2009-05-06 06:46 | alexmitin | Note Added: 0003118 | |
2011-03-21 21:26 | 2useven10 | File Added: viewer.diff | |
2011-03-21 21:27 | 2useven10 | File Added: hex-view-far3.PNG | |
2011-03-21 21:28 | 2useven10 | Note Added: 0006540 | |
2011-03-21 21:37 | alexy | Note Added: 0006541 | |
2011-03-21 21:41 | 2useven10 | Note Added: 0006543 | |
2011-03-21 21:43 | 2useven10 | Note Edited: 0006543 | |
2011-03-21 21:51 | alexy | Assigned To | => alexy |
2011-03-21 21:51 | alexy | Status | new => assigned |
2011-03-21 22:06 | alexy | Note Added: 0006546 | |
2011-03-21 22:06 | alexy | Status | assigned => feedback |
2011-03-21 22:29 | 2useven10 | Note Added: 0006547 | |
2011-03-21 23:19 | alexy | Build | => 1925 |
2011-03-21 23:19 | alexy | Note Added: 0006548 | |
2011-03-21 23:19 | alexy | Status | feedback => closed |
2011-03-21 23:19 | alexy | Resolution | open => fixed |
2011-03-21 23:19 | alexy | Fixed in Version | => 3.0 |