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 07:30 | alexy | Relationship added | has duplicate 0000826 |
| 2009-05-05 09:11 | vskirdin | Relationship added | has duplicate 0000854 |
| 2009-05-06 05: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 |