Работа с текстовыми строками в Lazarus

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

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

Ответить
fflatx
незнакомец
Сообщения: 5
Зарегистрирован: 27.09.2008 01:14:24

Работа с текстовыми строками в Lazarus

Сообщение fflatx »

Всем здравствуйте.

Учусь заочно в университете. Для решения учебной задачи создаю небольшое приложение для обработки строк (или текстовых файлов). В связи с проблемами лицензирования в университете отсутствует Дельфи и используется Лазарус. Соответственно задачу решаю в этой среде. ОС Linux Mandriva 2008.0, Лазарус 0.9.24, собран для gtk2+, чтобы работал русский интерфейс.
Трудность состоит в том, что в университете установлена Windows XP, следовательно имеется проблема с кодировками. Для моей задачи это критично, т.к. задача связана именно с обработкой текстовых данных.

Собственно проблема: в Дельфи и паскале переменные типа string представляют собой массив символов. И к отдельному символу можно обратиться по индексу. В Лазарусе при работе с кириллицей это срабатывает правильно для однобайтовых кодировок. Для UTF-8, используемой в Linux это не работает. Т.е. в Windows это работает, в Linux - нет.
Запуск Лазаруса в локали CP1251
$ LC_ALL=ru_RU.CP1251 ./startlazarus
а также использование функций UTF8toAnsi, UTF8decode не дают никакого эффекта. Символы в строке как были двухбайтовыми, так и остались.


Воспользовался гуглом, поиском по форуму, обнаружилось множество вариантов, вплоть до написания собственных модулей, но это показалось мне слишком громоздким. Я понимаю, что при желании можно написать любую функцию, но для чего тогда нужны все эти UTF8toAnsi, UTF8decode, если человек в результате пишет свою функцию?
Можно под вайном запустить виндовый Лазарус (и даже Дельфи), но все эти варианты я оставляю на крайний случай.
Я пытаюсь разобраться в проблеме, использовать кроссплатформенность Лазаруса и возможности тех функций, которые есть.

Возможно ли разрулить эту ситуацию, не прибегая к написанию собственных функций и использования виндовых инструментов? В чем моя ошибка? Прошу помочь мне разобраться, и по возможности пнуть меня в нужную сторону. :D
Честно говоря, почитав соответствующие темы форума, я слегка запутался...
Кстати, может быть, эта тема обобщит накопленный опыт форумчан по этой теме.

P.S. Прошу модераторов сильно не бить меня за создание этой темы.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
Сообщения: 1409
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Работа с текстовыми строками в Lazarus

Сообщение Sergei I. Gorelkin »

Если уж переопределять локаль, то LC_ALL=ru_RU.CP1251 нужно указывать при запуске собственной программы, а не Лазаруса.
UTF8ToAnsi, как следует из названия, преобразует из utf-8 в текущую локаль системы, т.е. по умолчанию в линуксе получится опять utf-8. Кроме того, для ее работы к программе нужно подключать модуль cwstring.
UTF8Decode преобразует utf-8 в WideString (строку с двухбайтовыми символами, к которым можно обращаться по индексу - по крайней мере до тех пор, пока речь не зайдет об экзотике типа древнеегипетских иероглифов). Если результат присвоить переменной типа string, то компилятор вставит автоматическое преобразование и общий результат будет в точности таким же, как у UTF8ToAnsi.
Наконец, недавно в FPC добавили модуль iconvenc, позволяющий перекодировать с явным указанием исходной и целевой кодировок. Но он пока что не кроссплатформенный (только для *nix, хотя в природе есть и iconv.dll для Windows)
Все сказанное, собственно, относится только FPC и его библиотекам, но не к Лазарусу. Лазарус (точнее, LCL), надо полагать, имеет свой собственнй набор средств. Но пускай о нем расскажут люди более сведущие.
Odyssey
энтузиаст
Сообщения: 580
Зарегистрирован: 29.11.2007 16:32:24

Re: Работа с текстовыми строками в Lazarus

Сообщение Odyssey »

Если кодировка исходных текстовых файлов не имеет значения, то можно хранить их в UTF-8, и обрабатывать в ней же. Работа с ней не намного сложнее, нужно только помнить про переменное число байт на символ. В модуле LCLProc есть функции для работы с UTF-8, например UTF8Pos, UTF8Copy, UTF8Delete, UTF8Insert, UTF8Length, а посимвольный доступ вместо s[i] можно получить с помощью UTF8CharStart(PChar(s), Length(s), i). Скорость будет несколько ниже чем при обработке ANSI-строк, но ее можно увеличить оптимизацией алгоритмов обработки.

Lazarus 0.9.26 в качестве внутренней кодировки LCL будет использовать UTF-8, поэтому опыт работы с ней может еще пригодится.
Logo
постоялец
Сообщения: 464
Зарегистрирован: 20.08.2008 01:00:47

Re: Работа с текстовыми строками в Lazarus

Сообщение Logo »

Sergei I. Gorelkin писал(а):Наконец, недавно в FPC добавили модуль iconvenc, позволяющий перекодировать с явным указанием исходной и целевой кодировок.

Спасибо, очень помог.
yad2000
незнакомец
Сообщения: 2
Зарегистрирован: 25.12.2012 11:34:58

Re: Работа с текстовыми строками в Lazarus

Сообщение yad2000 »

У меня аналогичная проблема (+/-).
Версия Lazarus 1.0.4.
Ось: W7x64

Пишу так:

Код: Выделить всё

procedure TForm1.addnewchars(var A:string;str:string); //Кусок 1
begin
   A:=A+UTF8Copy(str,Length(str),2); //Нужно к примеру 2-ой символ из str добавить A - буква "Б" - в этом примере/ и второй вопрос как правильно добавить все символы из str, обычный плюс почему то не добавляет
end;

procedure TForm1.init_mysymbols_var; //Кусок 2
begin
  mySymbols:= '';
  addnewchars(mySymbols,'АБГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя')); //Русский алфавит
end; 

procedure TForm1.Button3Click(Sender: TObject); //Кусок 3
begin
  Edit1.Text:=UTF8CharStart(PChar(mySymbols),1,codesymb); //codesymb меняется по таймеру от 0 до (UTF8Length(mySymbols)-1)
end;   


Итог: не работает, перерыл файл LCLProc - выход не нашел, прошу помощи здесь.

Добавление вначале этих строк - не помогло:
{$mode objfpc}
{$H+}
{$codepage UTF8}

Замена всех string на Unicodestring не помогло
Обращение к mySymbols обычными средствами и средствами UTF8xxxxx разных функций не помогло во множестве сочетаний

Использую шрифт в проекте и во всех компонентах @Arial Unicode MS

Ну и наконец вопросы:
1. Как все таки перебрать строку содержащую русские буквы посимвольно (?), в Delphi таких проблем не было
2. Как перебирать строку посимвольно содержащую не только русские буквы но и всякую экзотику Unicode. Допустим есть строка Unicodestring из 30 экзотических символов, и как получить доступ к любому из них?


В файле LazUTF8 есть функция:

Код: Выделить всё

function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
var
  CharLen: LongInt;
begin
  Result:=0;
  while (ByteCount>0) do begin
    inc(Result);
    CharLen:=UTF8CharacterLength(p);
    inc(p,CharLen);
    dec(ByteCount,CharLen);
  end;
end; 

которая вызывается функцией:

Код: Выделить всё

function UTF8Length(const s: string): PtrInt;
begin
  Result:=UTF8Length(PChar(s),length(s));
end;   

Это все наводит на мысль что к каждому символу нужно обращаться по байтам, но если есть строка содержащая символы разной длины в байтах, как определить это кол-во байт для нужного номера символа в массиве, изначально не зная что это за символ??? Вообще такое сделать реально???
SSerge
энтузиаст
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Работа с текстовыми строками в Lazarus

Сообщение SSerge »

yad2000 писал(а):{$codepage UTF8}


А вот нельзя в лазарусе это делать, как и применять соответствующий ключ компиляции. А если все же сделать, то внимательно трассировать что получается. И компилятор должен быть 2.7.х, в 2.6.0 нет нормально работающих UnicodeStrings.
Если очень хочется, см. здесь: http://sirserge.altai.info/articles/?id=45
yad2000 писал(а):1. Как все таки перебрать строку содержащую русские буквы посимвольно (?),


Код: Выделить всё

Var symbol:string;

begin
for i:=1 to Utf8Length(s) do begin
   symbol:=UTF8Copy(s,i,1);
   if (symbol='Я') then ....;
end;


yad2000 писал(а): Как перебирать строку посимвольно содержащую не только русские буквы но и всякую экзотику Unicode. Допустим есть строка Unicodestring из 30 экзотических символов, и как получить доступ к любому из них?


Еще раз, UnicodeString мягко говоря несовместим с библиотеками LCL.
Лучше работать со строками UTF8 (тип string! ни в коем случае не UTF8String!). Посимвольный доступ как в предыдущем примере.
yad2000
незнакомец
Сообщения: 2
Зарегистрирован: 25.12.2012 11:34:58

Re: Работа с текстовыми строками в Lazarus

Сообщение yad2000 »

SSerge - огромное спасибо за пол пинка ))) все заработало, 2 дня не мог разобраться :mrgreen:

теперь мой код принял вид:

Код: Выделить всё

//////////////////////////////////
function getUTFSymbol(str:string;i:integer):string; //Функция достает нужный мне символ по номеру
var rez:string;
begin
 rez:= UTF8Copy(str,i,1);
 Result:=rez;
end;
//////////////////////////////////
procedure TForm1.addnewchars(var A:string;str:string); //Функция добавляет str к A с проверкой уже имеющихся в A символов
var i:integer;
begin
  for i:=1 to Utf8Length(str) do
    begin
     if UTF8Pos(UTF8Copy(str,i,1),A)=0 then
     A:=A+UTF8Copy(str,i,1);
    end;
end; 
//////////////////////////////////
procedure TForm1.init_mysymbols_var; // процедура инициирует на старте программы строку mySymbols:string
begin
  //SetLength(mySymbols,1);
  mySymbols:= #39;     //Апостроф
  addnewchars(mySymbols,'!"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~');  //Основная латиница
  addnewchars(mySymbols,'±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'); //Дополнительная латиница-1
  addnewchars(mySymbols,'ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ'); //Расширенная латиница A
 addnewchars(mySymbols,'ƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏ'); //Расширенная латиница B
  addnewchars(mySymbols,'ɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯ');  //Фонетические знаки         
end; 


Пока все работает, если что - еще напишу ))
Ответить