Размер строки в байтах (вне зависимости от кодировки)

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение zub » 23.10.2013 15:28:05

>>Вроде как {$codepage UTF8} указывает компилятору на то, в какой кодировке находится исходник, то есть в какой кодировке компилятор получает строковые константы из исходника, и на работу готовой программы не влияет.
Источники не помню, вроде читал чтото у SSerge, там говорилось что если кодировка исходников известна ({$codepage UTF8} или файл с BOM) то компилятор перекодирует исходные строки в UTF16 и хранит их внутри в этом виде. Если нет ни бома ни директивы, компилятор хранит строки в исходном виде помечая как CP_NONE - т.е. без кодировки.
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение SeZuka » 23.10.2013 18:36:09

qivi писал(а):Вобще то влияет, как минимум при этой директиве Length возвращает в символах длину строк типа UnicodeString, а без неё в байтах. Так же при этой директиве все операторы для работы со строкой работают со строками типа UnicodeString посимвольно.

Ерунду не говорите.

Вот небольшой тест:
Код: Выделить всё
program Project1;
{$codepage UTF8}
var s: UnicodeString;
begin
  s := 'qwerty';
  WriteLn(s);
  WriteLn(Length(s) * SizeOf(s[1]));
  s := 'йцукен';
  WriteLn(s);
  WriteLn(Length(s) * SizeOf(s[1]));
  ReadLn(s);
  WriteLn(s);
  WriteLn(Length(s) * SizeOf(s[1]));
  ReadLn;
end.

его вывод:
Код: Выделить всё
qwerty
12
щЎєъхэ
12
яяяяяя
яяяяяя
12

убираем {$codepage UTF8}, получаем:
Код: Выделить всё
qwerty
12
?????????╡??
24
яяяяяя
яяяяяя
12

Почему константа с кириллицей стала в два раза больше?

Потому что писали в лазарусе, который сохранил исходник в UTF-8, а в UTF-8 на символ кириллицы приходится 2 байта, а для латиницы 1. А компилятор без директивы {$codepage UTF8} всосал строку как есть, т.е. каждый байт - 1 символ, а потом еще этот каждый байт перевел в 2 байта для UnicodeString. Вот и получили хз что на выходе и размер в два раза больше.

Замените в примере UnicodeString на обычный String, и попробуйте с {$codepage UTF8} и без нее, получите тот же эффект.
SeZuka
постоялец
 
Сообщения: 209
Зарегистрирован: 05.09.2012 14:58:05

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение SSerge » 23.10.2013 18:43:35

zub
Господа, вы отвлекаетесь от первоисточников и начинаете обсуждать легенды. А по первоисточнику Type UnicodeString=AnsiString(65001); то есть AnsiString, маркированное кодовой страницей UTF16. Хотя, исторически более правильно сказать, скорее UCS2 - подмножеством UTF16, в котором используются только двухбайтные символы. Потому что происходит от наследия Windows NT и его WideStrings.

Первоисточник законодателя мод, наверное, это: http://docwiki.embarcadero.com/RADStudi ... ring_Types

The AnsiString structure contains a 32-bit length indicator, a 32-bit reference count, a 16-bit data length indicating the number of bytes per character, and a 16-bit code page.

"Структура AnsiString содержит 32-битный индикатор длины, 32-битный счетчик ссылок, 16-битное число, показывающее число байт на представление одного символа и 16 бит поля кода страницы."


Второй первоисточник: fpcsrc/rtl/inc/astrings.inc:

This file contains the implementation of the AnsiString type,
and all things that are needed for it.
AnsiString is defined as a 'silent' pchar :
a pchar that points to :

@-16 : Code page indicator.
@-12 : Character size (2 bytes)
@-8 : SizeInt for reference count;
@-4 : SizeInt for size;
@ : String + Terminating #0;
Pchar(Ansistring) is a valid typecast.
So AS[i] is converted to the address @AS+i-1.

Constants should be assigned a reference count of -1
Meaning that they can't be disposed of.



Строки, кстати, действительно заканчиваются нулевым терминатором. Во всех кодировках.
В этом же источнике можно посмотреть, как именно манипулирует со строками RTL сама по себе.


ЗЫ:
Главная проблема freepascalя imho не столько уникодные строки как таковые, а колоссальное падение производительности при работе с ними, вкупе с безобразной политикой компилятора постоянно порождать промежуточные переменные, приводящее к поистине ужасающим результатам, когда программа без тщательной оптимизации и подгонки под логику компилятора тотально сливает по быстродействию C#, jav'e и даже прости господи перлу с PHP.
SSerge
энтузиаст
 
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение qivi » 24.10.2013 09:33:03

SeZuka, я не говорю ерунды, возьмите и попробуйте, я не знаю как оно там реализованно на низком уровне и допускаю что излагаю безграмотно, но по факту оно работает так как я и сказал. А именно UnicodeString + {$codepage UTF8} и работаем с ней как с обычной анси (это касается не только Length), однако при записи в файл через поток строк содержащих кирилицу и латиницу корректен расчёт 2 байта на символ (значит утф16).

Я не знаю на каком это уровне: реализация строки или неявные перекодировки при выполнении, - по существу мне важнее понимать как теперь этим пользоваться, а не как это устроено.
Аватара пользователя
qivi
энтузиаст
 
Сообщения: 703
Зарегистрирован: 19.01.2009 13:45:54
Откуда: Россия

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение SSerge » 24.10.2013 10:39:31

Если судить по описаниям товарищей из эмберкадеры, в ейном Delphi XE внутренний формат строковых данных отличается от такового в текущем freepascal.
http://docwiki.embarcadero.com/RADStudi ... RAD_Studio

Поэтому давайте на внутренний формат представления данных всё таки не полагаться;
SSerge
энтузиаст
 
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение hinst » 30.10.2013 21:56:19

так нормальный способ получить длину строки в байтах есть что ли или нет, надо самому велосипедировать?
Умножать на SizeOf(s[1]) как-то не очень подходит для UTF-8
мне лично приходит такая идея: Length(RawByteString(s))
единственный минус тут в том, что скорее всего, на каждый вызов будет создаваться копия строки. Или не будет???

Добавлено спустя 3 минуты 55 секунд:
в принципе, если для UTF-8 строк компилятор в области данных для длины строки (где-то там s[-3..0]) хранит длину строки в символах, то по идее получить длину UTF-8 строки в байтах нельзя никак, кроме как идти по строке до нулевого символа. Если она нигде не хранится
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение zub » 30.10.2013 22:18:54

>>Умножать на SizeOf(s[1]) как-то не очень подходит для UTF-8
Откуда дровишки?
Сколько было высказываний про переменную длинну символа... приведите пример когда length*sizeof врет?
Имхо такого примера быть неможет - length*sizeof железный метод, проглотит и анси и уникоде и старые строки которые [255]
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение hinst » 30.10.2013 22:33:03

zub врёт - смотря что ты хочешь определить.
С выключенным {$CodePage UTF8} у меня показывает длину строки в байтах.
С включённым {$CodePage UTF8} у меня показывает длину строки в символах.
Таким образом в первом случае не получишь длину строки в символах, а во втором случае не получишь длину строки в байтах.
Код: Выделить всё
program project1;

{$CodePage UTF8}

uses
  SysUtils;

var
  s: string;

begin
  s := 'йцы123';
  WriteLN(Length(s) * SizeOf(s[1]));
  WriteLN(
    IntToStr(
      PLongint( @(s[1]) - 4 )^
    )
  );
end.

без codepage 9, 9
с codepage 6, 6

всё проверяю на FPC 2.6.2

Добавлено спустя 2 минуты 1 секунду:
потому что "йцы" - по 2 байта, а "123" - по одному
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение zub » 30.10.2013 22:48:41

zub врёт - смотря что ты хочешь определить.

чтоб сделать заключение мало инфы - в какой кодировке исходниики?

zamtmn@desktop:~/tmp$ ./project1
9
9
zamtmn@desktop:~/tmp$ ./project1
9
9

на 2.6 глянуть к сожалению немогу, вот вывод примера для 2.7.1 первый с директивой, второй без, исходник в utf8 - не врет.
Для Length нету разницы сколько символов - он возвращает в байтах всегда (для утф16 в парахбайт), SizeOf нужен только чтоб отличить стринг от юникодестринг, механизм полностью аналогичен вычислению объема динамического массива, то что символы переменной длинны - безразницы

Добавлено спустя 51 минуту 23 секунды:
предлагаю чуток модернизировать програмку чтоб показывала что сидит внутри строки. исходник сошряняем в utf8 без бом:
Код: Выделить всё
program project1;

{$CodePage UTF8}

uses
  SysUtils;

var
  s: string;
  i:integer;
  pb:pbyte;

begin
  s := 'йцы123';
  WriteLN('Length(s)='+inttostr(Length(s)));
  WriteLN('SizeOf(s[1])='+inttostr(SizeOf(s[1])));
  WriteLN('(@(s[1])-p)^='+IntToStr(PLongint(@(s[1])-sizeof(pointer))^));

  writeln('s[i]:');
  for i:=1 to length(s) do
                          write(inttostr(ord(s[i]))+';');

  writeln(#13#10'Bytes:');
  pb:=@s[1];
  for i:=1 to Length(s) * SizeOf(s[1]) do
  begin
    write(inttostr(pb^)+';');
    inc(pb);
  end;

end. 

вывод на 2.7.1 с директивой и без не отличается и вызлядит так:
zamtmn@desktop:~/tmp$ ./project1
Length(s)=9
SizeOf(s[1])=1
(@(s[1])-p)^=9
s[i]:
208;185;209;134;209;139;49;50;51;
Bytes:
208;185;209;134;209;139;49;50;51;

меняем string на unicodestring:
вывод без директивы:
zamtmn@desktop:~/tmp$ ./project1
Length(s)=9
SizeOf(s[1])=2
(@(s[1])-p)^=9
s[i]:
208;185;209;134;209;139;49;50;51;
Bytes:
208;0;185;0;209;0;134;0;209;0;139;0;49;0;50;0;51;0;

вывод с директивой:
zamtmn@desktop:~/tmp$ ./project1
Length(s)=6
SizeOf(s[1])=2
(@(s[1])-p)^=6
s[i]:
1081;1094;1099;49;50;51;
Bytes:
57;4;70;4;75;4;49;0;50;0;51;0;

ИМХО всё логично. директива {$CodePage UTF8} (или BOM в файле) указывает компилятору кодировку исходников, на основе нее компилятор перекодирует строки в исходниках в свою внутренюю кодировку, либо оставляет как есть. length`у и sizeof`у до лампочки символы, они оперируют байтами и словами для utf8 и utf16 соответственно. Разве в 2.6.3 не так?
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение SeZuka » 31.10.2013 05:57:27

Видимо уважаемый hinst никак не может понять назначения директивы {$CodePage UTF8} и что она никак не влияет на работу строковых функций. Все уже было разжевано мною выше.
SeZuka
постоялец
 
Сообщения: 209
Зарегистрирован: 05.09.2012 14:58:05

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение SSerge » 31.10.2013 07:43:02

SeZuka
Она (директива) как раз влияет, ибо заставляет компилятор при присвоении объявленных в тексте программы строковых констант строковым переменным, для которых кодовая страница не задана, принудительно маркировать их указанной в директиве страницей, что в свою очередь включает механизм преобразования кодовых страниц при присвоении переменным, маркированным другой кодовой страницей.

Соответственно, функция length например, начинает считать длину строки в единицах, определенных по отрицательному смещению как "symbol width in bytes". То есть, для UnicodeString это будет два байта. Для всех остальных касающихся нас (не исследовал всйкие там тайские и японские кодинги), в том числе и utf8 (пока) - 1 байт.

Для WideString по описанию:

Length also supports arguments of type PCharand PWideChar, in which case it is identical to the StrLen and WStrLen functions, respectively. In this case, the function actually calculates the length of the null-terminated string


то есть - БАЙТЫ.

Код: Выделить всё
{$codepage UTF8}
{$H+}

program testw;
Var s,k:string;
    su,ku:UnicodeString;
begin
   s:='АБВГДЕЖЗИЙ';
   k:='1234567890';
   su:=s;
   ku:=k;

   writeln('length of s:',s,' = ',length(s));
   writeln('length of k:',k,' = ',length(k));
   writeln('length of su:',su,' = ',length(su));
   writeln('length of ku:',ku,' = ',length(ku));
   
end.



Результат работы, текст с директивой:

Код: Выделить всё
length of s:АБВГДЕЖЗИЙ = 20
length of k:1234567890 = 10
length of su:АБВГДЕЖЗИЙ = 10
length of ku:1234567890 = 10


А вот теперь то же самое без директивы и в исходнике вымаран BOM:

Код: Выделить всё
length of s:Р?Р'Р'Р"Р"РР-Р-Р?РT = 20
length of k:1234567890 = 10
length of su:Р?Р'Р'Р"Р"РР-Р-Р?РT = 20
length of ku:1234567890 = 10


У вас тестовый примерчик того... по самой сути некорректен
SSerge
энтузиаст
 
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение zub » 31.10.2013 08:15:18

И в чем влияние? разница только в содержимом строк что обясняется "неверным" перекодированием - в UTF16 попадает текст UTF8 без перекодирования.
>>Соответственно, функция length например, начинает считать длину строки в единицах, определенных по отрицательному смещению как "symbol width in bytes"
она ее и так и так считает в этих еденицах, т.к. в других неумеет

>>У вас тестовый примерчик того... по самой сути некорректен
все примерчики в теме вполне актуальны и наглядны. только почемуто некоторые трактуют результаты "вмеру своей испорченности"))
меняем string на unicodestring:
вывод без директивы:
zamtmn@desktop:~/tmp$ ./project1
Length(s)=9
SizeOf(s[1])=2
(@(s[1])-p)^=9
s[i]:
208;185;209;134;209;139;49;50;51;
Bytes:
208;0;185;0;209;0;134;0;209;0;139;0;49;0;50;0;51;0
;

вывод программы из моего поста выше - ясно видно как UTF8 "разползся" по utf16 не перекодируясь.
имеем:
1 - length не учитывает составные символы и мериет в байтах или словах, sizeof соответственно возвращает то в чем померил length в байтах или словах
2 - вся кажущаяся разница в случаях с\без бом\директива сидит внутри строки, а не в поведении length\sizeof

считаете не так? дайте осмысленный пример, а не из разряда тут 10, там 20 и пара домыслов
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение SSerge » 31.10.2013 09:21:08

zub писал(а):И в чем влияние? разница только в содержимом строк что обясняется "неверным" перекодированием - в UTF16 попадает текст UTF8 без перекодирования.


Не только в этом разница. Еще разница в маркёрах кодовой страницы, которые в строковых переменных файле без директивы у вас отстутствуют, а в файле с директивой {$codepage} имеются. Что при работе с функциями например из lcl вызовет кошмар и каку из-за скрытой конверсии.

zub писал(а):а не в поведении length\sizeof


Однако, некто упорно утверждает, что length всегда отражает длину строки в байтах, что неверно. На самом деле length отражает длину строки в элементах, и поведение может меняться, в зависимости от того, ЧТО для строки считается элементом. ToDo например обещает для строк utf8 сменить модель, чтобы позволять их прямое посимвольное индексирование. Так что не стоит рассчитывать на сохранность того, что есть сейчас.
SSerge
энтузиаст
 
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение zub » 31.10.2013 09:43:48

>>Не только в этом разница
это понятно, маркер кодировки теперь "встроен" в строки. но емнип он меняется только для string`ов, для unicodestrinп`ов он cp_utf16 без оглядки на бомы-директивы.

>>ToDo например обещает для строк utf8 сменить модель, чтобы позволять их прямое посимвольное индексирование. Так что не стоит рассчитывать на сохранность того, что есть сейчас.
Вот это будет черный день((. где можно ознакомится с этим тодо?
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Размер строки в байтах (вне зависимости от кодировки)

Сообщение SSerge » 31.10.2013 10:15:09

zub писал(а):где можно ознакомится с этим тодо?


А х.з., текстовка попадалась года полтора назад, я сейчас даже не могу с уверенностью сказать, что она касалась именно fpc. Возможно, это были розовые планы эмбаркадеры, причем довольно бессмысленные.

zub писал(а):unicodestrin`ов он cp_utf16 без оглядки на бомы-директивы.


ну да, он всегда для них постоянен. Но. "При присвоении немаркированной строки к переменной с маркером, включается механизм преобразования, и присваиваемой строке присваивается кодовая старница по умолчанию, после чего производится конверсия в utf16", т.е. гарантированный способ испортить содержимое строки. imho, если в тексте есть переменные unicodeString - директива выбора кодовой страницы должна быть включена в обязательном порядке.
SSerge
энтузиаст
 
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Пред.След.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 223

Рейтинг@Mail.ru