И снова строки и кодировки в виндовсе
Модератор: Модераторы
И снова строки и кодировки в виндовсе
Хорошую гадость добавили в 3 версии компилятора, неругательных слов нет. Словил в винде перекодирование строк из 866 кодировки в многобайтную. Даже rawbytestring при операциях типа A5:=A5+#171; перекодируется и длина A5 получается больше 1 . Программа большая и хз на что компилятор возбуждается и включает это перекодирование. Как же эту дрянь победить?
https://www.freepascal.org/docs-html/cu ... html[quote]
To declare a variable as a string, use the following type specification:
If there is a codepage specifier, (using round brackets) it indicates an ansistring with associated code page information.
[/quote]
и там же:
https://www.freepascal.org/docs-html/cu ... 2-44[quote]
Since strings have code page information associated with them, it is important to know which code page a string uses:
Short strings always use the system code page.
Plain ansistrings use the system code page.
Single byte strings with declared code page use that code page.
The RawBytestring type has no code page information associated with it.
Constant strings have the code page of the source file. If none is specified the system codepage is used (CP_ACP).
See Programmer’s Guide, {$CODEPAGE } directive.[/quote]
Добавлено спустя 7 минут 46 секунд:
Добавлено спустя 10 минут 51 секунду:
Судя по https://www.freepascal.org/docs-html/cu ... efsu6.html SizeOf(#число) должно быть всегда 1 байт.
Тогда, что же дает StringCodePage(a5) (Word) непосредственно перед кривым присваиванием?
To declare a variable as a string, use the following type specification:
Код: Выделить всё
... : type string ( unsigned integer )
... : type AnsiString ( unsigned integer )
[/quote]
и там же:
https://www.freepascal.org/docs-html/cu ... 2-44[quote]
Since strings have code page information associated with them, it is important to know which code page a string uses:
Short strings always use the system code page.
Plain ansistrings use the system code page.
Single byte strings with declared code page use that code page.
The RawBytestring type has no code page information associated with it.
Constant strings have the code page of the source file. If none is specified the system codepage is used (CP_ACP).
See Programmer’s Guide, {$CODEPAGE } directive.[/quote]
Добавлено спустя 7 минут 46 секунд:
Может в A5 уже что-то есть? Т.е. до присваивания Length(A5)>0, соответственно и после присваивания Length(A5)>1?Сквозняк писал(а):A5:=A5+#171; перекодируется и длина A5 получается больше 1
Добавлено спустя 10 минут 51 секунду:
Судя по https://www.freepascal.org/docs-html/cu ... efsu6.html SizeOf(#число) должно быть всегда 1 байт.
Тогда, что же дает StringCodePage(a5) (Word) непосредственно перед кривым присваиванием?
Глюки присваивания зависят от места в коде. Пока доэкспериментировал, они стали глючить чуть по другому.
Вроде бы номер кодировки у переменной меняется. Кто её меняет, может другой поток, когда с подобным типом работает, хз. В линуксе, на компиляторе 2.64 и с типом ansistring этот же код работал идеально. Если бы в винде не было проблем с кодировкой в консоли и системных командах, то сидел бы и там на этой версии.
Код: Выделить всё
interface
TYPE
{$IFDEF WINDOWS}
STROKI1 = RAWBYTESTRING;
Код: Выделить всё
UU5,a5: RAWBYTESTRING;
O5: STROKI1;
writeln('____a5____',stringcodepage(a5));
a5:='';
a5:=#171;
writeln('a5 ',length(a5),' ',ord(a5[1]));
if length(a5)>1 then writeln(ord(a5[1]));
writeln('____a5-2____');
O5:='';
O5:=O5+#171;
WRITELN('____________0----738_O5 ',LENGTH(O5),' ',STRINGCODEPAGE(O5));
IF LENGTH(O5)>1 THEN WRITELN('____________0----738_O5-2 ',ORD(O5[1]),' ',ORD(O5[2]));
writeln('____0_0____');
a5:='';
a5:=#171;
writeln('a5 ',length(a5),' ',ord(a5[1]));
if length(a5)>1 then writeln(ord(a5[1]));
writeln('____0____',stringcodepage(a5));
a5:='';
a5:=#171;
writeln('a5 ',length(a5),' ',ord(a5[1]));
if length(a5)>1 then writeln(ord(a5[1]));
writeln('____0_ ___');
writeln('____1____',stringcodepage(O5));
O5:='';
O5:=#171;
writeln('O5 ',length(O5),' ',ord(O5[1]));
if length(O5)>1 then writeln(ord(O5[1]));
writeln('____1_ ___');
Код: Выделить всё
____a5____65001
a5 1 171
____a5-2____
____________0----738_O5 2 65001
____________0----738_O5-2 208 187
____0_0____
a5 1 171
____0____866
a5 1 171
____0_ ___
____1____65001
O5 1 171
____1_ ___
Мне кажется, что виновато представление #171 попробуй Chr(171).
( Я вожусь с парсингом и ребилдом html в разных кодировках и если бы простое сложение строк вызвало перекодировку я бы это (ИМХО) сразу заметил.)
( Я вожусь с парсингом и ребилдом html в разных кодировках и если бы простое сложение строк вызвало перекодировку я бы это (ИМХО) сразу заметил.)
Да какая разница, как впихивать. Сам этот тест произошёл от того, что в виндовой сборке, на третьей версии компилятора часть текста превратилась в каракули. В программе несколько потоков и кодировок, и как видно из теста, итоговая кодировка в типе переменной плавает, от того и глюки. Поменял в глючном месте на CHR
Перекодирование сработало. На этой версии компилятора чтобы это перекодирование победить, наверно надо добавлять в компиляторе переменную запрещающую эти перекодирования. Запретил перекодирования и кодировки в переменных хаотично меняются, но не вредят, как этого хотели вредители - лепота. По сути, сейчас безопасного типа строковых переменных в новом компиляторе нет, потому что rawbytestring это тот же ansistring с другой кодировкой, а кодировка может хаотично сама поменяться, если в программе есть для этого условия
Плохо, что нужно искать, где в компиляторе это автоматическое перекодирование прописано, а потом патчить и пересобирать компиляторы вместе с лазарусами - звиздец, сколько работы бракоделы устроили.
Добавлено спустя 3 часа 14 минут 11 секунд:
А вот и демку смены кодировки строки напейсал. Скомпилил и протестил в вайне.
Содержимое сборочного скрипта k.cmd
Запускал из среды, в линуксовой консоли konsole, сделанной командой:
Демка!
Результат работы:
Кодировка строки сменяется после setconsolecp(866); но не сразу. И с какого хрена она вообще была 1251, если кодировка исходника 866, а CP_NONE=65535.
Добавлено спустя 10 минут 3 секунды:
Потом попробую в отдельном потоке всё время работы программы спамить командой
Вдруг данные перестанут портиться 
Код: Выделить всё
writeln('____a5-2____');
O5:='';
O5:=O5+chr(171); //#171;
WRITELN('____________0----738_O5 ',LENGTH(O5),' ',STRINGCODEPAGE(O5));
IF LENGTH(O5)>1 THEN WRITELN('____________0----738_O5-2 ',ORD(O5[1]),' ',ORD(O5[2]));
Код: Выделить всё
____a5-2____
____________0----738_O5 2 65001
____________0----738_O5-2 208 187
Добавлено спустя 3 часа 14 минут 11 секунд:
А вот и демку смены кодировки строки напейсал. Скомпилил и протестил в вайне.
Содержимое сборочного скрипта k.cmd
Код: Выделить всё
C:lazarus-2.0.10-fpc-3.2.0-win64fpc3.2.0binx86_64-win64fpc.exe -FuC:lazarus-2.0.10-fpc-3.2.0-win64lclunitsx86_64-win64win32 -FuC:lazarus-2.0.10-fpc-3.2.0-win64lclunitsx86_64-win64 -FuC:lazarus-2.0.10-fpc-3.2.0-win64componentslazutilslibx86_64-win64 -FuC:lazarus-2.0.10-fpc-3.2.0-win64packagerunitsx86_64-win64 -dLCL -dLCLwin32 -Twin64 -Xm -WN -XX -CX 1.ppКод: Выделить всё
WINEDEBUG=fixme-all,err-all wine cmd.exeКод: Выделить всё
{$codepage CP866}
uses
WINDOWS, SYSUTILS;
type
//RawByteString2 = type AnsiString(CP_NONE); //С ЭТОЙ СТРОКОЙ ТОЖЕ САМОЕ
RawByteString2 = type RawByteString;
VAR
Q3: BYTE;
A5: RAWBYTESTRING;
B5: RawByteString2;
Q2,W2,E2: LONGINT;
BEGIN
Q2:=StringCodePage(B5);
WRITELN('____Q2=',Q2);
Q3:=138;
A5:=CHR(Q3);
WRITELN(LENGTH(A5));
setconsolecp(866);
A5:=A5+#171;
B5:='';
B5:=B5+#171;
WRITELN(LENGTH(A5),' ',LENGTH(B5),' ',CP_NONE,' ',StringCodePage(A5),' ',StringCodePage(B5)); // ... 65535
FOR W2:=1 TO 500 DO BEGIN
E2:=StringCodePage(B5);
B5:=#171;
IF E2<>Q2 THEN WRITELN(length(B5),' !!!!!!1!____E2=',E2,' W2=',W2);
Q2:=E2;
setconsolecp(866);
E2:=StringCodePage(B5);
B5:=#171;
IF E2<>Q2 THEN WRITELN(length(B5),' !!!!!!2!____E2=',E2,' W2=',W2);
Q2:=E2;
SLEEP(2);
END;
WRITELN('________________________ ',length(B5));
END.
Код: Выделить всё
____Q2=1251
1
2 1 65535 1251 1251
1 !!!!!!2!____E2=866 W2=1
________________________ 1
Добавлено спустя 10 минут 3 секунды:
Потом попробую в отдельном потоке всё время работы программы спамить командой
Код: Выделить всё
setconsolecp(866);>>если кодировка исходника 866
имеется ввиду {$codepage CP866}?
это работает не так.
codepage указывает кодировку исходного текста файла где встречена данная директива и только помогает верно перекодировать захардкоженые строки в DefaultSystemCodePage
>>Демка!
Без обид, но демка альтернативно одаренная, сделай по человечески
имеется ввиду {$codepage CP866}?
это работает не так.
codepage указывает кодировку исходного текста файла где встречена данная директива и только помогает верно перекодировать захардкоженые строки в DefaultSystemCodePage
>>Демка!
Без обид, но демка альтернативно одаренная, сделай по человечески
А нужно чтобы rawbytestring-и всех видов вообще не перекодировались, как и было обмануто в вики. Одно дело, когда данные из строк выводятся в консоль или используются как внешняя команда, а ОС, винда конечно, портят данные перекодированием и нужно как-то это компенсировать. А совсем другое дело, когда данные циркулируют внутри программы, а какой-то баг в компиляторе их портит, если его заинтересует значение строковых переменных. Это какой-то бандитизм встроенный в компилятор: гоп стоп, предъявите данные, сейчас мы их "обработаем" и вернём в испорченном виде.zub писал(а): и только помогает верно перекодировать захардкоженые строки в DefaultSystemCodePage
Обычный выхлоп из процедур занимающий минимум места в консоли и который можно понять видя код. По другому там делать вообще пользы мало, потому что врительны из разных потоков могут пересекаться, а часть вообще не долетать до консоли, а вот такие меченые фразы быстро делать и можно искать поисковиком. Что-то переделывать есть смысл, только если есть практический смысл, всё равно же никто прямо сейчас не станет исправлять баг компилятора.zub писал(а):Без обид, но демка альтернативно одаренная, сделай по человечески
- Снег Север
- долгожитель
- Сообщения: 3067
- Зарегистрирован: 27.11.2007 15:14:47
- Контактная информация:
Чтобы не перекодировалось используют TByteArray.
Но это же вообще не строки!Снег Север писал(а):Чтобы не перекодировалось используют TByteArray.
Код: Выделить всё
Type
TByteArray = Array of Byte;Все работает как надо.
>>Просто переписывать с паскаля в подобие Си.
Серьезно? ты умудряешся из паскаля помойку восклицательных знаков, прочерков и "значащих" имен переменных сделать))
Давай по пунктам, просто и без каракулей. что не так?
>>И с какого хрена она вообще была 1251, если кодировка исходника 866, а CP_NONE=65535.
повторяю, кодировка исходника и $CODEPAGE это разные вещи и работают только на стадии компиляции для захардкоженых строк
чтобы понять откуда вылезла 1251 - смотрим внутрь StringCodePage и внимательно вдумываемся что там происходит когда на входе пустая строка (nil)
смысл не держать все строки в какойто кодировке а дать компилятору возможность правильно определить их начальную кодировку и в дальнейшем перекодировать их в требуемую
>>Просто переписывать с паскаля в подобие Си.
Серьезно? ты умудряешся из паскаля помойку восклицательных знаков, прочерков и "значащих" имен переменных сделать))
Давай по пунктам, просто и без каракулей. что не так?
>>И с какого хрена она вообще была 1251, если кодировка исходника 866, а CP_NONE=65535.
повторяю, кодировка исходника и $CODEPAGE это разные вещи и работают только на стадии компиляции для захардкоженых строк
чтобы понять откуда вылезла 1251 - смотрим внутрь StringCodePage и внимательно вдумываемся что там происходит когда на входе пустая строка (nil)
Код: Выделить всё
program Project1;
{$Codepage cp866}
var
rusAcode866:byte;
begin
rusAcode866:=128;
writeln('',' CP=',StringCodePage(''));//DefaultUnicodeCodePage потому что строки нет
writeln(chr(rusAcode866),' CP=',StringCodePage(chr(rusAcode866)));//DefaultUnicodeCodePage потому что строка создана в рантайме как результат вызова функции
writeln(chr(128),' CP=',StringCodePage(chr(128)));//cp866 потому что строка создана в компайлтайме из символа и указано что исходник "какбудто" в 866
writeln(#128,' CP=',StringCodePage(#128));//cp866 потому что строка создана в компайлтайме из символа и указано что исходник "какбудто" в 866
readln;
end. - Снег Север
- долгожитель
- Сообщения: 3067
- Зарегистрирован: 27.11.2007 15:14:47
- Контактная информация:
Я вас удивлю, но строки - это и есть массивы байтов, только со специальной обработкой и, возможно, дополнительными полями (короткие строки так на 100% байты). поэтому, если вы хотите получить необработанный набор байтов от источника, то получайте и храните именно как набор байтов, а потом уже конвертируйте в свои строки с нужной вам кодировкой. В делфи это делается через функции класса TEncoding.Сквозняк писал(а):Но это же вообще не строки!
Ничего подобного. Раскидано море граблей и в нём нужно найти в нём лазейку. Одну часть работы со строками в винде исправишь - в другой что-то отвалится. Там починишь - ещё где-то. А в линуксе со строками всё пректасно, потому что его предок уникс был придуман фанатом игр для своей игры, а не жадным кровопийцей ради бабла.zub писал(а):Все работает как надо.
Ну, ну, я тоже могу написать хелловорд, а потом хвастаться, как он красиво работает. А в реальном программе нужно усперь виндовую консоль растянуть, а всё что промоталось, то пропало - не прочитаешь. И необходимо не только чтобы внутри программы данные не сломались, но и чтобы в консоль писалось не кракозябрами и ещё чтобы команды не ломались на файлах и каталогах с неанглийскими символами. Это как выбор между быстро, качественно и дёшево, но когда выбрать нужно не одно, а сразу все три, а тебе кто-то при этом ещё и вредит постоянно. Ты не показал как сделать правильно а только придираешься к рабочему коду, который ещё нужно десятки раз тестировать и переделывать прежде чем переписать в чистовик. Самая минимальная демка необходимого уже после кучи человекочасов работы выглядит вот так:zub писал(а):Серьезно? ты умудряешся из паскаля помойку восклицательных знаков, прочерков и "значащих" имен переменных сделать))
Код: Выделить всё
{$codepage CP866}
USES
SYSUTILS, WINDOWS;
TYPE
STROKI1 = type AnsiString(866);
STROKI2 = type AnsiString;
VAR
Q5: STROKI1;
A5,S5: STROKI2;
Z5: array of AnsiString;
Info: TSearchRec;
Q2,W2: LONGINT;
BEGIN
W2:=DEFAULTSYSTEMCODEPAGE;
WRITELN('______DEFAULTSYSTEMCODEPAGE=',DEFAULTSYSTEMCODEPAGE);
SetMultiByteConversionCodePage(866);
SETLENGTH(Z5,2);
Z5[0]:='/c';
Z5[1]:='chcp 866';
SysUtils.ExecuteProcess('cmd.exe',Z5,1);
setconsolecp(866);
WRITELN('__Q5__ ',StringCodePage(Q5));
WRITELN('__A5__ ',StringCodePage(A5));
Q5:=#171;
A5:=#171;
WRITELN('__Q5__ ',StringCodePage(Q5),' ',LENGTH(Q5),' ',ORD(Q5[1]));
WRITELN('__A5__ ',StringCodePage(A5),' ',LENGTH(A5),' ',ORD(A5[1]));
A5:='GF*'; //НАЧАЛО ИМЕНИ КАТАЛОГА, ДРУГАЯ ЧАСТЬ СОДЕРЖИТ ПРОБЕЛ И НЕЛАТИНСКИЕ СИМВОЛЫ
S5:='SLUCILASJ LAZA';
Q2:=SYSUTILS.findfirst(A5,faDirectory+faAnyFile+fahidden+fareadonly,Info);
IF Q2=0 THEN S5:=INFO.NAME;
SYSUTILS.FINDCLOSE(Info);
WRITELN('__INFO.NAME__ ',StringCodePage(S5),' ',LENGTH(S5),' ',S5);
A5:=S5+'1*'; //ИЩЕМ ФАЙЛ В КАТАЛОГЕ
S5:='SLUCILASJ LAZA';
Q2:=SYSUTILS.findfirst(A5,faDirectory+faAnyFile+fahidden+fareadonly,Info);
IF Q2=0 THEN S5:=INFO.NAME;
SYSUTILS.FINDCLOSE(Info);
WRITELN('__INFO.NAME__ ',StringCodePage(S5),' ',LENGTH(S5),' ',S5);
END.А теперь смотри в исходники:повторяю, кодировка исходника и $CODEPAGE это разные вещи и работают только на стадии компиляции для захардкоженых строк
чтобы понять откуда вылезла 1251 - смотрим внутрь StringCodePage и внимательно вдумываемся что там происходит когда на входе пустая строка (nil)
Код: Выделить всё
{$define FPC_HAS_ANSISTR_CONCAT}
procedure fpc_AnsiStr_Concat (var DestS:RawByteString;const S1,S2 : RawByteString{$ifdef FPC_HAS_CPSTRING};cp : TSystemCodePage{$endif FPC_HAS_CPSTRING}); compilerproc;
Var
S1Len, S2Len: SizeInt;
same : boolean;
S1CP, S2CP, DestCP: TSystemCodePage;
begin
{$ifdef FPC_HAS_CPSTRING}
DestCP:=cp;
if DestCp=CP_NONE then
DestCP:=DefaultSystemCodePage;
{$else FPC_HAS_CPSTRING}
DestCP:=StringCodePage(DestS);
{$endif FPC_HAS_CPSTRING}
DestCP:=TranslatePlaceholderCP(DestCP);
{ if codepages are different then concat using unicodestring,
but avoid conversions if either addend is empty (StringCodePage will return
DefaultSystemCodePage in that case, which may differ from other addend/dest) }
if S1='' then
S1CP:=DestCP
else
S1CP:=StringCodePage(S1);
S1CP:=TranslatePlaceholderCP(S1CP);
if S2='' then
S2CP:=DestCP
else
S2CP:=StringCodePage(S2);
S2CP:=TranslatePlaceholderCP(S2CP);
{$ifdef FPC_HAS_CPSTRING}
{ if the result is rawbytestring and both strings have the same code page,
keep that code page }
if (cp=CP_NONE) and
(S1CP=S2CP) then
DestCP:=S1CP;
{$endif FPC_HAS_CPSTRING}
if (S1CP<>DestCP) or (S2CP<>DestCP) then
begin
ansistr_concat_complex(DestS,S1,S2,DestCP);
exit;
end;
{ only assign if s1 or s2 is empty }
if (S1='') then
begin
DestS:=s2;
exit;
end;
if (S2='') then
begin
DestS:=s1;
exit;
end;
S1Len:=Length(S1);
S2Len:=length(S2);
{ Use Pointer() typecasts to prevent extra conversion code }
if Pointer(DestS)=Pointer(S1) then
begin
same:=Pointer(S1)=Pointer(S2);
SetLength(DestS,S1Len+S2Len);
if same then
fpc_pchar_ansistr_intern_charmove(PAnsiChar(DestS),0,DestS,S1Len,S2Len)
else
fpc_pchar_ansistr_intern_charmove(PAnsiChar(S2),0,DestS,S1Len,S2Len+1)
end
else if Pointer(DestS)=Pointer(S2) then
begin
SetLength(DestS,S1Len+S2Len);
fpc_pchar_ansistr_intern_charmove(PAnsiChar(DestS),0,DestS,S1Len,S2Len+1);
fpc_pchar_ansistr_intern_charmove(PAnsiChar(S1),0,DestS,0,S1Len);
end
else
begin
SetLength(DestS,S1Len+S2Len);
fpc_pchar_ansistr_intern_charmove(PAnsiChar(S1),0,DestS,0,S1Len);
fpc_pchar_ansistr_intern_charmove(PAnsiChar(S2),0,DestS,S1Len,S2Len+1);
end;
SetCodePage(DestS,DestCP,false);
end;
{$endif FPC_HAS_ANSISTR_CONCAT}Это было бы так, если бы rawbytestring был не тем же ansistring, а реально кодировкой, в которую не лезут воры и бандиты, чтобы испортить данные. А нет так как сейчас: " А есть ли у вас данные в строках? А если найду?"смысл не держать все строки в какойто кодировке а дать компилятору возможность правильно определить их начальную кодировку и в дальнейшем перекодировать их в требуемую
Добавлено спустя 5 минут 6 секунд:
А паскаль, это ассемблер со спецобработкой. Ага. Некоторые люди ближнему будут и стрихнин нахваливать и предлагать употребить, если его официально объявят полезным и неоюбходимым продуктом. Я не на кого не намекая, но как говорил Мойша в анекдоте, жёлтую муху не обманешь.Снег Север писал(а):Я вас удивлю, но строки - это и есть массивы байтов, только со специальной обработкой и, возможно, дополнительными полями (короткие строки так на 100% байты). поэтому, если вы хотите получить необработанный набор байтов от источника, то получайте и храните именно как набор байтов, а потом уже конвертируйте в свои строки с нужной вам кодировкой. В делфи это делается через функции класса TEncoding.
Компилятор на выходе своих процедур рожает строки с DefaultSystemCodePage - так и задумано. RawByteString в параметрах - указание что сюда придут неперекодированные строки = то что передал програмист, но кодировка какаято у них всеравно будет. Все проблемы с кодировками (ну почти все) от того что компилятор не смог определить кодировку захардкоженых строк (програмист приложил к этому руку) и дальше все ломается
Это не головой думано а совсем другим местом. Но что поделать, в мире же параллельно идёт деградация и инволюция, потому в код регулярно вываливаются помои.zub писал(а):Компилятор на выходе своих процедур рожает строки с DefaultSystemCodePage - так и задумано.
Нет, не все. Проблем этим произволом породили множество. Теперь внутри бинарника происходят битвы вампиров и оборотней, война строк, в которых все стремятся покусать и заразить всех. Раньше были нормальные строки, а в третьей версии компилятора стали разносчиками заболеваний. А ещё, этим заявлением "используйте массивы, самки собак" декларируется, что мне запретили использовать функционал строковых функций там, где раньше широко использовал. На буржуйском форуме тоже было много вопросов про строки, фраза про тэбите-массивы - оттуда. С головой у европейцев туго, души моллюсков плохо прижились.zub писал(а):Все проблемы с кодировками (ну почти все) от того что компилятор не смог определить кодировку захардкоженых строк (програмист приложил к этому руку) и дальше все ломается
>>Это не головой думано а совсем другим местом
Критика в таком ключе допустима если если умеешь или знаешь как лучше.
Судя по FPC_HAS_CPSTRING компилятор можно собрать без поддержки кодировок, но не интересовался в эту сторону
Критика в таком ключе допустима если если умеешь или знаешь как лучше.
Судя по FPC_HAS_CPSTRING компилятор можно собрать без поддержки кодировок, но не интересовался в эту сторону
