Net-Base списание

19.04.2026

Миграција на Unicode на старите Delphi-проекти: замки, стратегија и чиста имплементација

Многу Delphi-постоечки апликации сè уште користат ANSI-стрингови. Миграцијата на Unicode тогаш е повеќе од еден прекинувач на компајлерот: таа се однесува на пристапот до податоци, интерфејсите, извештаите, библиотеки од трети страни и тестирањето. Овој напис покажува практичен пат за миграција – вкл...

19.04.2026

Миграцијата на Unicode на старите Delphi-проекти е во многу компании неопходен чекор, бидејќи постоечките апликации инаку сè повеќе наидуваат на ограничувања при меѓународни податоци, модерни оперативни системи, интеграции и нови интерфејси. Во пракса тоа ретко е „повторно компилирање и готово“. Delphi од Unicode-верзиите (од Delphi 2009) внесе фундаментални промени во стандардните string-типови. Со тоа се поместуваат претпоставките за кодирање на знаци, распоред во меморија и API-сигнатури. Кој го потценува тоа, произведува тивко појавување на грешки во податоците, уништени експорти, нејасни случаи за поддршка и ризици за безбедноста.

Овој напис нуди технички поткрепен пристап: како да го анализирате постоечкото, како смислено да го ограничите опсегот, како да ги намалите ризиците на горешки точки (бази на податоци, датотеки, Windows-APIs, COM, REST-услуги) и како да ја зацврстите миграцијата така што оперативната работа и понатамошниот развој може да течат паралелно. Фокусот е на типичните замки на Delphi во VCL-апликации, сервиси и интерфејси – со поглед на патиштата за модернизација, во кои подоцна можат да се вклучат теми како BDE-замена со нативна поврзаност, REST-сервери или мултиплатформен развој.

Зошто префрлувањето на Unicode во Delphi често е „поголемо отколку што се мисли“

Во класичните верзии на Delphi string беше ANSI-String (во зависност од системската codepage). Од Delphi 2009 string е подразбирано UnicodeString (UTF-16). Истовремено многу библиотеки и VCL-класи беа префрлени на Wide-API. Тоа е принципијелно позитивно, затоа што ја поддржува меѓународната азбука поотпорно. Но: legacy-кодот често е изграден со претпоставки како „1 знак = 1 бајт“, „PChar е PAnsiChar“ или „Length() одговара на број на бајтови“.

Типичните причини зошто миграциите стануваат посложени:

  • Имплицитни конверзии минуваат низ кодот, но ги менуваат податоците (особено кај датотеки, интерфејси или BLOB/TEXT-полња во бази).
  • Бајт-ориентиран код (Streams, буфери, хеширање, криптирање) станува невидливо погрешен кога содржината на string-ови се интерпретира како бајти.
  • Компоненти од трети страни делумно се само ANSI, или користат сопствени string-типови и callbacks.
  • Надворешната околина (Windows-APIs, COM, печатење/репортирање, EDI, CSV, XML/JSON) очекува одредени енкодинзи.

Целта затоа не треба да биде „што е можно помалку менување“, туку да се промени целенасочено таму каде што мора да се дефинираат текови на податоци и енкодинзи. Чиста Unicode-миграција е и можност да се документираат и тестираат нејасните граници на кодирањето.

Технички основи: Delphi-string-типови, енкодинзи и нивните нусефекти

string, UnicodeString, AnsiString, WideString – што навистина брои во проектот

За миграцијата е пресудно кои типови се користат на интерфејсите и во критичните функции:

  • string: Од Delphi 2009 е UnicodeString (UTF-16, со reference-counting, immutable семантика преку Copy-on-Write).
  • AnsiString: Бајт-стринг со прикачена codepage (во зависност од верзијата на Delphi може да се носи codepage). Подобар е кога надворешен интерфејс експлицитно бара одредено 8-битно кодирање.
  • UTF8String: Во поновите верзии на Delphi често како алјас/AnsiString со UTF-8 codepage; практичен за REST/JSON и многу протоколи.
  • WideString: BSTR (COM), управувано со меморијата преку SysAllocString; денес најчесто потребен само за одредени COM-interop случаи.
  • PChar: По Unicode-Delphi PWideChar. Тоа е една од најчестите точки на распаѓање при Windows-API повици.

Кога овие типови се мешаат, настануваат конверзии. Некои се точни, други изненадувачки: конверзијата е „исправна“ само ако знаете која codepage доаѓа од изворот и која се очекува на целта.

UTF-16 внатрешно, UTF-8 надвор: практичен модел

Во Delphi VCL-апликации често е разумно внатрешно да се работи последователно со string (UTF-16). Надвор (HTTP/REST, датотеки, messaging) во реалноста доминира UTF-8. Робустна линија е:

  • Внатре: string/UnicodeString како стандард.
  • Граници: при влез/излез експлицитно конвертирајте преку TEncoding.UTF8 (или дефинирани ANSI-codepage).
  • Бајт-базирана обработка: користете TBytes наместо Strings.

Тоа го намалува бројот на имплицитни конверзии и ја прави проверливата одговорност: „Каде се од бајтови прави текст, и со кое кодирање?“

Инвентаризација: каде Unicode кај старите Delphi-проекти типично се крши

Пред да го допрете кодот, исплати се структурирана инвентаризација. При Unicode-миграцијата на старите Delphi-проекти изворите на грешки обично не се рамномерно распоредени, туку се концентрирани во неколку горешки точки.

1) Пристап кон база и типови на полиња (BDE, ADO, FireDAC)

Многу старите проекти и натаму користат BDE или постари слоеви за пристап до податоци. Проблемите овде честопати се:

  • Доделување на charset-и на базите на податоци кон Delphi-string-овите (ANSI vs. Unicode-полња).
  • „Текст“ во BLOB-ови или Memo-полња без дефинирано кодирање.
  • SQL-стейтменти како String-ови што се толкуваат поразлично при умлати/Unicode-знаци.

Ако веќе се планира модернизација, Unicode-миграцијата може лесно да се поврзе со прочистување на пристапот до податоци, на пр. кон BDE-Ablosung mit nativer Anbindung и јасна конфигурација на charset-и (како кај PostgreSQL или MariaDB). Важно: миграцијата не треба автоматски да принудува миграција на базата, но интерфејсот помеѓу DB и Delphi мора да биде недвосмислен.

2) Датотечен и Stream I/O: CSV, INI, сопствени формати, увоз/извоз

Класика: порано датотеките се читаа/запишуваа преку AssignFile/ReadLn, TFileStream или TStringList.LoadFromFile без да се постави Encoding. Во Unicode-Delphi тогаш средината одлучува по хеуристика (BOM) или користи подразбирани енкодинзи. Тоа води до:

  • погрешно толкувани умлати (ä, ö) во CSV/лог-фајлови,
  • погрешни информации за должина во сопствени формати,
  • некомпатибилности со надворешни партнери што очекуваат ISO-8859-1 или Windows-1252.

Чиста решениe е за секој формат на датотека да се дефинира фиксен Encoding и тоа да се зацврсти во код и документација. За CSV/JSON UTF-8 вообичаено е правилна опција, за стари интерфејси понекогаш Windows-1252. Клучно е експлицитноста.

3) Windows-API, PChar, големини на буфер и обработка на пораки

Многу Delphi-апликации повикуваат WinAPI функции или работат со буфери. Чести места на распаѓање се:

  • Користење на PChar во комбинација со функции што имаат ANSI или Wide варијанти (…A/…W).
  • Големините на буфер се пресметуваат во бајти, но Char во UTF-16 е 2 бајта.
  • Pointer-arithmetic и Record-layouts што се базираат на 1-бајтните карактери.

Тука е потребно прецизно рефакторирање: или да се користат последователно Wide-API, или свесно да се повикува ANSI-варинтата и да се работи со AnsiString/соодветна codepage. „Некако се компилира“ не е критериум за квалитет.

4) COM, ActiveX, Office-Automation и библиотеки од трети страни

COM-интерфејсите често работат со BSTR (WideString). Старите верзии на Delphi имаа други подразбирани string-ови, па кодот „случајно“ се вклопуваше. Во Unicode-верзиите честопати се појавуваат двојни конверзии или погрешни претпоставки за тип во wrapper-ите. Компонентите од трети страни се исто така критични: некои даваат callbacks како PAnsiChar, други очекуваат null-терминирани бајт-стрингови.

Вреднувањето на зависностите е потребно: која библиотека е подготвена за Unicode, која не, и која може да се замени или да се капсулира? Капсулацијата често е најбрзиот пат да се концентрираат Unicode-стариотства во јасно огранчен слој.

Стратегија: Unicode-миграцијата на старите Delphi-проекти како контролирана програма за модернизација

Најсигурниот пристап е повеќестепена програма што ги прави ризиците видливи рано и ја одржува апликацијата работна додека се напредува.

Чекор 1: Дефинирајте опсег и приоритетизирајте code-hotspot-ови

Не секој изворен код треба веднаш да се менува. Приоритизирајте според текот на податоци и ризик:

  • Интерфејси кон надвор ({{NBML_TERM_7_50780f47}}-API, TCP/IP, датотеки, е-пошта, печатење/репортирање).
  • Пристап до податоци (SQL, ORM/Datamodule, BDE/FireDAC-слоеви).
  • Утилити функции блиски до string-ови (парсери, формати, енкодери/декодери).
  • Интеграции (COM, DLL-импорти, поврзувања со хардвер).

Резултатот треба да биде листа каде „Encoding е спецификација“. Тие места подоцна се прават тестабилни.

Чекор 2: Компилаторски/опции на проектот и warnings намерно засилете ги

Во многу проекти за години беа исклучени warnings. За Unicode-миграција тоа е контрапродуктивно. Паметно е да ги вклучите warnings и сериозно да ги земате предупредувањата за конверзија. Дополнително, помагаат проектни правила, на пр.: без имплицитни AnsiString-конверзии на I/O-границите, користење на TEncoding кај операциите со датотеки, без „PChar-трикчиња“ без јасен контекст.

Чекор 3: Внесете „Encoding-граници“ како технички слој

Практичен архитектонски потег е воведување мали adapter-и/helper-и што експлицитно дефинираат како надворешните податоци влегуваат и излегуваат. Примери:

  • CSV-Reader/Writer: секогаш со TEncoding.UTF8 (или дефинирана codepage) и јасни правила за сепаратор.
  • REST-Client/Server: JSON секогаш како UTF-8-бајти, header-ите правилно поставени, body не стримуван „базиран на string“.
  • Windows-API-wrapper: централни функции што чисто капсулираат Wide/Ansi.

Со тоа спречувате дека одлуките за Encoding се распрскаат низ целата кодна база.

Типични замки во кодот и како да се поправат чисто

Length, SizeOf, ByteLength: кога должината во знаци и големината во бајти се разделуваат

Во ANSI-времињата Length(s) често се злоупотребуваше како број на бајтови. Во UTF-16 тоа е погрешно. Ако ви требаат бајт-матрици, конвертирајте експлицитно:

  • За UTF-8: TEncoding.UTF8.GetBytes(s)
  • За дефинирана ANSI-codepage: TEncoding.GetEncoding(1252).GetBytes(s) (само ако е стручнo оправдано)

За големини на буфери кај API-повици важи: проверете дали функцијата очекува единици во знаци или бајти. Многу Wide-API очекуваат број на знаци, не бајти. Документацијата и сигнатурата одлучуваат, не интуицијата.

PAnsiChar vs. PWideChar: DLL-Imports и надворешни протоколи

Кај DLL-Imports опасноста е голема дека сигнатурите во Delphi-кодот повеќе не одговараат. Дефинирајте што DLL-то очекува:

  • Дали DLL-то очекува UTF-8? Тогаш е вообичаено да се предаде како PAnsiChar(UTF8String), но мора да ги контролирате животниот век и null-терминирањето.
  • Дали очекува UTF-16? Тогаш користете PWideChar и Wide-strings.

Во секој случај, импортираните функции треба да се капсулираат во посебна unit, за политиката за string да не се распрснува низ целиот проект.

Форматирање, конверзија на големи/мали, споредба: локал и нормализација

Unicode донесува и семантички теми: големи/мали букви не се тривијални во сите јазици, а знаците може да имаат различни нормализирани форми. Во типични корпоративни апликации тоа е помалку критично отколку во consumer-обработка на текст, но влијае на:

  • Сортирање и филтрирање (на пр. во Grids или функции за пребарување),
  • Case-insensitive споредби за клучни вредности,
  • Генерирање на имиња на датотеки или идентификатори.

Важно е да имате јасно правило: што се „клучеви“ (на пр. шифри на артикли, кодови на клиенти) кои треба да останат ASCII-блиски, а што се „текстови“ кои треба да бидат целосно Unicode-способни? Оваа поделба го намалува последователните грешки.

GUI/репортирање: фонтови, печатење, PDF и однесување на компоненти

VCL од Unicode-верзиите е во основа Unicode-способен, но практиката зависи од компоненти и патишта за излез. Ризиците се појавуваат кај:

  • Постари report-engine-и или PDF-генератори што претпоставуваат ANSI,
  • Barcode/Label печатење што бара одредени codepage-и,
  • Хардкодирани фонтови или сетови на знаци.

Планирајте рани тестови со реални примерни податоци (имиња, места, посебни знаци, нелатински писма ако се релевантни). Вредноста не е во „може да работи со Unicode“, туку во доказот: „Овој излез е во нашиот контекст точен.“

Податоци и перзистенција: Unicode не завршува во кодот

Поставете чисто charset-и и collation-и во базите

Unicode-миграцијата е стабилна само ако базите на податоци и драјверите се правилно конфигурирани. Примери:

  • Кај PostgreSQL UTF-8 е вообичаено стандарден; сепак client-encoding и однесувањето на драјверот мора да се проверат.
  • Кај SQL Server разликата помеѓу VARCHAR и NVARCHAR е релевантна; погрешен избор на колона може да доведе до губење на знаци.
  • Кај MariaDB/MySQL Charset/Collation (на пр. utf8mb4) се клучни за да не се отсечат 4-бајтни знаци.

Во Delphi-кодот параметрите и типот на полињата треба да се користат така што Unicode не се „врќа назад“ преку конверзија. FireDAC генерално нуди подобра контрола отколку многу старите слоеви за пристап.

Legacy-формати на датотеки: правила за миграција наместо тивка конверзија

Ако вашата апликација со години создава датотеки (формати за извоз, архивски фајлови, сопствени структури), треба да дефинирате:

  • Кои постоечки фајлови остануваат „како што се“ и ќе се читаат правилно при внес?
  • Кои формати ќе се подигнат на UTF-8?
  • Дали има полиња за верзија/header-и за јасно растеретување помеѓу нови и стари фајлови?

Тивка конверзија без означување е ризична, бидејќи грешките често се откриваат дури подоцна. Подобро: верзионирање, јасна детекција и целенасочена миграција.

Осигурување на квалитет: тестови кои навистина ги пронаоѓаат Unicode-проблемите

Unicode-грешките се често зависни од податоците. Затоа не се доволни само „Happy Path“ тестови. Корисно е тест-сет кој ги опфаќа проблематичните точки:

  • Roundtrip-тестови: Увоз → Обработка → Извоз, потоа бит-по-бит споредба (за дефинирани формати).
  • DB-Roundtrip: запишување/читање текстови со умлати, акценти и евентуално нелатински знаци; проверка за еднаквост.
  • Интерфејс-тестови: REST-појави како UTF-8, правилни header-и, JSON-escaping, логирање.
  • Регресија: репродуцирање на стари податоци и типични кориснички сценарија, особено кај пребарување, филтрирање, сортирање.

За B2B-системи е исто така релевантно грешките да бидат набљудливи: логирањето не смее да ги уништи енкодинзите. Ако логовите се пишуваат како ANSI, во случај на грешка ќе го изгубите токму она што е потребно за дијагноза.

Планирање и напор: што всушност ја крева комплексноста

Напорот за Unicode-миграцијата на старите Delphi-проекти зависи помалку од „линии код“ и повеќе од зависностите и поврзаностите:

  • Многу интеграции (DLLs, COM, уреди, ERP/DMS/CRM) го зголемуваат напорот за проверка, бидејќи енкодингот е релевантен на секоја граница.
  • Историски формати (стари извози, прилагодени CSV-ови) бараат правила за миграција и стратегии за компатибилност.
  • Мешани верзии на Delphi или повеќе производи од ист коден штампа зголемуваат потребата за координација.
  • Стари слоеви за пристап до податоци (на пр. BDE) може индиректно да блокираат Unicode и да упатуваат на потреба за модернизација.

Во практика се покажува дека е добро да се стабилизира Unicode најнапред во јадрото и во критичните текови на податоци. Потоа модули се прилагодуваат чекор по чекор. Тоа ја намалува опасноста и спречува долги „Big Bang“ фази без release.

Вметнување во патиштата за модернизација: REST-API, сервиси, мултиплатформа

Unicode често е столб кога постоечката софтверска база треба да се модернизира. Типични прашања за продолжување се:

  • REST-сервер или REST-API: да се додадат (или да се обезбеди дека JSON/UTF-8 се правилно обработуваат).
  • Стабилно работење на Windows-услуги или Linux-услуги (логирање, конфигурациони фајлови, протоколи).
  • Чекор-по-чекор модернизација на UI во VCL, а подоцна евентуално мултиплатформени клиенти.

Важно е редоследот: ако градите нови интерфејси, правилата за енкодинг треба да се дефинираат однапред. Unicode-миграција „онеска“ додека се развива интерфејс може да доведе до тешко проверливи проблеми, бидејќи причина и ефект ќе бидат измешани.

За внатрешно поврзување во магазинот е соодветно да се постават прилепени теми како Delphi-модернизација, FireDAC-пристап до податоци или архитектура на REST-сервери како продлабочувачки статии, така што читателите може да продолжат кон следниот технички чекор.

Заклучок: Unicode-миграцијата е темa на ризик – со правилна метода станува планирачка

Unicode-миграцијата на старите Delphi-проекти не е козметичен апгрејд, туку корекција на основни претпоставки за текст, бајтови и интерфејси. Кој постапува структурирано, добива повеќе од „умлати што повторно работат“: тековите на податоци стануваат недвосмислени, интеграциите поотпорни, а подоцнежната модернизација (на пр. REST-сервери, сервиси, прочистување на бази) полесна, бидејќи енкодинзите повеќе не се случуваат имплицитно „каде-какде“.

Ако за вашата Delphi-апликација ви треба конкретен план за миграција, анализа на ризиците за горешките точки или помош при реализација, најбрзиот следен чекор е технички воведен разговор за вашите услови и зависности: Контакт.

Во стручниот контекст и Delphi Unicode Migration и Delphi Ansi Zu Unicode играат важна улога кога интеграциите, тековите на податоци и развојот треба да делуваат заедно.

Проект или модернизациска иницијатива: расправете со Net-Base.

Сподели објава

Споделете го овој пост директно.

LinkedIn, X, XING, Facebook, WhatsApp и е-пошта се веднаш достапни. За Instagram директно подготвуваме линк и краток текст.

Е-пошта

Instagram се отвора во нов таб. Линкот и краткиот текст претходно се копираат во меѓуспремникот.