все что связано с моей работой
Главная » Програмирование » Работа со строками в Lаzarus

Работа со строками в Lаzarus

После перехода с Delphi на СПО для меня вторая и очень (на момент возникновения) проблема была это работа со строками. Поэтому было потрачено какое-то время на разбор “почему же ты не работаешь” и “как же сделать”. Здесь заметки как же все таки надо делать.

Во первых директива {$H+} — это включение длинных строк, как в Delphi, что-то типа как PChar. Без этой директивы строки воспринимаются компилятором как в BP(TP)6.0, т.е. S[0] — длина строки, и максимальный размер строки 255 символов. (1 байт — хранит длину). При включенной опции компилятора $H (или $LONGSTRINGS ON), которая включена по-умолчанию, тип string — это на самом деле ansistring. А переменная, имеющая тип ansistring, является указателем. FPC автоматически разыменовывает (операция-шапочка ^) этот указатель, и делает некоторые другие преобразования. Если в этой переменной содержится nil — это и есть пустая строка, таково соглашение. Имеем указатель на начало строки и символ #0 в конце строки – это общее с PChar, и именно это позволяет такие преобразования:

var S:string; 
    P:PChar; 
begin 
  S:='длинная строка'; 
  P:=PChar(S); 
  ... 

Но PChar ни чего не знает про длину строки, и поэтому строка реально занимает на 8 байт больше. 4 байта перед указателем dword(P)-4, содержат длину строку, т.е. фактически длина строки ограничена MaxInt(longint), а не “бесконечна” как PChar. Первые 4 байта (перед длиной) dword(P)-8 это счетчик ссылок, для чего он да и вообще про организацию строк в Delphi очень хорошо описано в этой старой статье.

Вообще это было такое лирическое отступление, на самом деле string = ansistring ~ PChar не создает проблем.

Кодировка вот головная боль с которой придется столкнутся. В Delphi никогда не думал о кодировке, ибо Windows, все было прозрачно. Но Лазарь работает только с UTF8, т.е. все строковые типы находятся в кодировке UTF8. Про кодировку можно почитать тут. В двух словах и очень “ограничено”: все ASCII символы кодируются 1 байтом, т.е. с кодом символа<127, все остальные кодируются 2-мя байтами, т.е. русский текст.

Что из этого следует?

А вот что, первое это что надо везде использовать функции вида UTF8XXXX (UTF8Copy, UTF8Length, UTF8Pos и т.д). Надо помнить что Length теперь возвращает не кол-во символов, а кол-во байт в строке (без 8ми служебных), а кол-во символов возвращает UTF8Length.

Второе, для работы с файлами в которых есть русские буквы надо пользоваться тоже функциями с UTF8 (FindFirstUTF8, FileAgeUTF8 и т.д.).

Третье, текстовые файлы надо перекодировать в UTF8 чтоб они нормально показывались:

var F:TextFile;
    S:string;
begin
  if OD.Execute then begin
    AssignFile(F,UTF8ToSys(OD.FileName));
    Reset(F);
    while not eof(F) do begin
      readln(F,S);
      S:=AnsiToUtf8(S);
      Memo1.Append(S);
    end;
    CloseFile(F);
  end;

Пока все текстовые файлы загружаю так. Потому что, TStringList.LoadFromFile(…); а затем TStrings.Text:=AnsiToUtf8(TStrings.Text) не дает нужного преобразования, как и такая конструкция:

  ...
  S:=Memo1.Text;
  S:=AnsiToUtf8(S);
  Memo1.Text:=S;
  ... 

Возможно “что-то, где-то, как-то” но времени разбираться пока не было, на досуге посмотрю что не так с LoadFromFile и по экспериментирую с TStream.

Четвертое (и самое тяжелое для моего понимания было), вот такой кусок кода уже не будет работать в том понимании как мы привыкли:

for i:=0 to Length(S) do 
  if S[i]='П' then begin 
  ... 

Потому как S[i] – 1 байт, а русская буква ‘П’ – это 2 байта.

Продолжение следует…

Комментариев нет

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.