რა ვუყოთ ქართულ მთავრულ ასოებს უნიკოდში?

შარშან მაისში Unicode-მა ქართული მხედრული ანბანის 46 მთავრული ასონიშანი დაამტკიცა.

შეიძლება ჯერ კიდევ ადრეა, მაგრამ მომავალში ოპერაციული სისტემების განახლებებში მაინც აისახება ეს ცვლილება. ცნობისმოყვარეობის გამო გადავწყვიტე პატარა მოკვლევა გამეკეთებინა, თუ რას გვიცვლის ეს ამბები პროგრამისტებს და ამ პოსტში გაგიზიარებთ.

მანამდე ყოველი შემთხვევისთვის რამდენიმე განმარტება:
Unicode – სტანდარტი, რომელიც ყველა სიმბოლოს უნიკალური რიცხვითი კოდით განსაზღვრავს. ასევე აღწერს სხვადასხვა ენისთვის საჭირო სპეციფიურ წესებს. ამ სტანდარტმა მთელი ტექნიკური სამყარო მოიცვა და დღეს ყველა იყენებს მას, ვისაც ტექსტის დამუშავება / გამოსახვა სჭირდება – ოპერაციული სისტემები, პლატფორმები, ბრაუზერები…

UTF-8 – უნიკოდს სიმბოლოების და რიცხვების სია აქვს, თუმცა არ ანაღვლებს, როგორ მოხდება ამ ინფორმაციის შენახვა მეხსიერებაში. ამისთვის კოდირების სხვადასხვა ალგორითმი არსებობს. UTF-8 ერთ-ერთი და ყველაზე გავრცელებული მათგანია, რადგან ოპტიმალურად იყენებს ადგილს და ზედმეტ ბაიტებს არ გამოყოფს სიმბოლოსთვის, თუ ის ერთ ცალშიც ეტევა. კოდირების სხვა მაგალითებია UCS-2, UTF-16, UTF-32…

სტანდარტში ცვლილებები იმპლემენტაციებში ცვლილებებს იწვევს, რაც არც ისე სწრაფად ხდება. მაგალითად, ₾ ლარის სიმბოლო უნიკოდის მერვე ვერსიაში დაემატა 2015 წლის 17 მაისს და ვინდოუსის განახლება ამ სიმბოლოს ასახვისთვის 2016 წლის 19 იანვარს გამოვიდა.

ოპერაციულებმა უნდა განაახლონ კლავიატურის დრაივერი, რომ მომხმარებლებს CAPS რეჟიმის ჩართვით შეეძლოთ მთავრულის წერა. და ასევე უნდა განაახლონ სისტემური ფონტები, რომ font fallback-ის დროს სწორი სიმბოლოები გამოჩნდეს.

იმის გამო, რომ მთავრულის და არამთავრულის ერთსა და იმავე სიმბოლოს სხვადასხვა კოდი აქვს, პროგრამისტებს რაღაცების გათვალისწინება გვიწევს ხოლმე – აქამდე მხოლოდ სხვა ენებისთვის, ახლა კი ქართულისთვისაც. მაგალითად, როცა ხდება სტრიქონების შედარება, ძებნა, regex პატერნები, სორტირება, ბაზაში შენახვა და ა.შ.

 

მონაცემთა ბაზა

MS SQL server-ს ჩაშენებული აქვს უნიკოდის მხარდაჭერა და ოპერაციებისას ისედაც სტანდარტს უყურებს, მთავარია სწორ ვერსიას უყუროს: SQL Fiddle

აი, MySQL-თან ცოტა სხვაგვარადაა – აქ თითოეულ ბაზას, ცხრილს ან საერთოდ ველს შეიძლება ჰქონდეს განსაზღვრული კონკრეტული collation (წყობა?), იმის მიხედვით თუ რა ტიპის ინფორმაციას ინახავს. ჩვენ დაჩვეულები ვართ, რომ utf8_general_ci გამოვიყენოთ, რომელსაც ქართული ასოებიც ‘ესმის’. ეს collation სრულყოფილად არ აკეთებს უნიკოდის იმპლემენტაციას utf8_unicode_ci-ისგან განსხვავებით. უბრალოდ მეტი წარმადობისთვის იყენებდნენ. ახლანდელი პროცესორების პირობებში დიდი სხვაობა აღარ არის. სამაგიეროდ, utf8_unicode_ci ქართულ წყობას კარგად გაიგებს.

აი, მაგალითს მოვიყვან:
უნიკოდს კოდებთან ერთად განსაზღვრული აქვს სიმბოლოების მიმდევრობაც, რომელსაც სორტირებისას ვიყენებთ. მაგალითად ამ სიაში ყველა ქართული ‘ა’ სიმბოლო მიყოლებით არის – ნუსხურიც, ასომთავრულიც და მხედრულიც. მათ შემდეგ მოდის ‘ბ’ ასოს ვარიანტები. სავარაუდოდ ახალი მთავრულიც ანალოგიურად დაემატება.

SQL Fiddle

CREATE TABLE IF NOT EXISTS `test` (
  `content` varchar(200) NOT NULL
) DEFAULT CHARSET=utf8 COLLATE utf8_general_ci;
INSERT INTO `test` (`content`) VALUES
  ('აბგ'),  ('ააააა'),  ('Ⴁააააა'),  ('Ⴀააააა'),  ('bcd'),  ('ab.'),  ('Ⴄ'),  ('ж'),  ('Ж'),  ('ц'),  ('Ц');
  

CREATE TABLE IF NOT EXISTS `test_better` (
  `content` varchar(200) NOT NULL
) DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
INSERT INTO `test_better` (`content`) VALUES
  ('აბგ'),  ('ააააა'),  ('Ⴁააააა'),  ('Ⴀააააა'),  ('bcd'),  ('ab.'),  ('Ⴄ'),  ('ж'),  ('Ж'),  ('ц'),  ('Ц');


select * from `test` d order by d.content;
select * from `test_better` d order by d.content;

შედეგი:

ab., bcd, Ж, ж, ц, Ц, Ⴀააააა, Ⴁააააა, Ⴄ, ააააა, აბგ
ab., bcd, ж, Ж, ц, Ц, ააააა, Ⴀააააა, აბგ, Ⴁააააა, Ⴄ

ახლა რომ MySQL 8-ის ბეტა რელიზი გამოვიდა, იქ ჯერ უნიკოდის მეცხე ვერსიაა. ჩვენი მთავრული ასოები კი მეთერთემეტეშია 🙂

 

ჯავასკრიპტი

მართალია ბევრი რეალიზაცია არსებობს, მაგრამ V8-ს ვერ ავცდებით, ამიტო მაგის მიხედვით განვიხილავ.

ჯავასკრიპტს უნიკოდის მხარდაჭერა კი აქვს, თუმცა რაღაცები მაინც პრობლემურია (მაგალითად რეგექსი). თუ საიტზე გვჭირდება სორტირება ან შედარება (მაგალითად თუ ფილტრები გვაქვს), მაშინ ჩვეულებრივი სორტირება არ გამოგვადგება და Locale უნდა მივუთითოთ, რომ უნიკოდის წესები გათვალისწინოს:
მაგალითად,

let a = ['აბგ','ააააა','Ⴁააააა','Ⴀააააა','bcd','ab.','Ⴄ','ж','Ж','ц','Ц'];
console.log(a.sort());
console.log(a.sort(Intl.Collator('ru').compare));

ქართული Collation-ის მხარდაჭერა სამწუხაროდ საერთოდ არ აქვს. ამიტომ ნუსხურთან და ასომთავრულთან ერთად სწორად ვერ დაასორტირებს. ნუ, ეს მაინც ძალიან იშვიათი შემთხვევაა სადარდებლად რომ ღირდეს. სტანდარტულად, code point-ების მიხედვით ალაგებს და ანბანის მიხედვით გამოვა, მხოლოდ მთავრულ “ა” ასოს ჩვეულებრივი “ჰ”-ს მერე დასვამს ან პირიქით.

ამ პრობლემას სხვა ენებში სტრიქონების ერთ რეგისტრში გადაყვანით ვჭრით. გიორგიმ იდეა მომაწოდა:

myArray.sort(function(s1,s2){ return s1.toLowerCase() > s2.toLowerCase()}));

ქართულზეც სავარაუდოდ ეგრე იმუშავებს, მას შემდეგ რაც V8-ში უნიკოდის იმპლემენტაცია განახლდება. ახლა მაგალითად ასომთავრულზე და ნუსხურზე ეგრეა: "Ⴀ".toLowerCase() => "ⴀ"

როგორც ჩანს, იმის გამო რომ სტანდარტში ასომთავრულს CAPITAL აწერია, ხოლო ნუსხურს SMALL, ის ნუსხურის მთავრულ ვარიანტად არის იმპლემენტირებული (ეს v8 source ფაილი: unicode.cc, კოდებია პირდაპირ შესაბამებული.)
მხედრული ახლა caseless არის. საინტერესოა როგორი აღნიშნვა ექნება. მგონი სხვა ენა არ არსებობს, რომელსაც ორნაირი მთავრული ასოები აქვს.
Anyway, ამასაც ვერსიის განახლება სჭირდება.

ახლა გამახსენდა, რომ V8 საერთოდ open source პროექტია და მოხალისეს შეუძლია საერთოდ ქართული locale დაამატოს. ჯერ-ჯერობით ამის შედეგი ცარიელია:

Intl.Collator.supportedLocalesOf('ka')

 

ჯავა

ჯავაც არ ჩქარობს განახლებებს. JDK 9 უნიკოდის მერვე ვერსიით (სადაც ლარის სიმბოლო დაემატა) ორი წლის შემდეგ – 2017 წლის სექტემბერში გამოვიდა.
აქ სტრიქონების შედარება ხდება equals მეთოდით. მომავალში ქართულისთვისაც მოგვიწევს რომ equalsIgnoreCase გამოვიყენოთ:

"Ⴀ".equals("ⴀ")  => false
"Ⴀ".equalsIgnoreCase("ⴀ")  => true

ერთი მთავრული რადგან უკვე გვაქვს, მაგის გამოყენებით ვტესტავ, უბრალოდ პრაქტიკაში არ ვიყენებთ მაგ ასოებს.

ასევე რეგექსის დროსაც პირდაპირ ვერ მოვძებნით. ჩვეულებრივი i – ignore case არ გამოგვადგება, უნიკოდს სხვანაირად ამუშავებს. ამიტო:

"A".matches("(?i)[a]")  => true
"Ⴀ".matches("(?i)[ⴀ]") => false

Pattern.compile("[ⴀ]", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE).matcher("Ⴀ").matches();  => true

შესაბამისად ყველგან სადაც სადაც სტრიქონებს ვიყენებთ, გათვალისწინება იქნება საჭირო – maps, sets, etc.

 

PHP

აქ საერთოდ უნიკოდთან ცუდი სამუშაოა + დაემატება აქაც გადაყვანა-გადმოყვანა.

 

 
კიდევ ცვლილება დაგვჭირდება ერთ ადგილას – ძებნის ძალიან მოსახერხებელ ხელსაწყოებში – grep და მისთანები. grep-ის case insensitive ოპცია ახლაც არ მუშაობს ქართულ ასომთავრულ ასოებზე. იმედია მაგათ ვერსიების განახლებაშიც აისახება, უებარი პროგრამებია დიდ (და პატარაც) ტექსტებში და ფაილებში რეგექსით საძებნად.

ბევრი აქაური სისტემა მაშინვე ვერც განაახლებს ალბათ პლატფორმებს, რადგან დიდი ტესტირება დასჭირდება. მაგათ ალბათ ფრონტში მოუწევთ ვალიდატორების და კონვერტერების დაყენება, რომ მომხმარებლის მთავრული ასოები ძველ ჯავაში ან სხვაგან კარგად წავიდეს.

საერთო ჯამში, მე მომწონს რომ მთავრული დაამატეს (რამდენიმე ადამიანის დიდი შრომის შედეგად). ქართული ენის ნაწილია და არ უნდა დაიკარგოს.

თქვენ ხომ არ გაქვთ იდეები, კიდევ რაზე აისახება ეს ცვლილება?

ბმულები მთავრული ასოების შესახებ:
On.ge – UNICODE-მა ქართული მხედრული ანბანის 46 მთავრული ასონიშანი დაამტკიცა
DevFest 2016: Akaki Razmadze –  ❤  [I LOVE UNICODE]
DevFest 2017: Akaki Razmadze – გუტენბერგი, სტივ ჯობსი, გუგლი, ხინკალი, უნიკოდი
DevFest 2016: Michael Everson – The confusing case history of Georgian in Unicode

DevFest 2017 გამოსვლა: Continuous Integration-Delivery-Deployment

დეველოპერების ფესტივალის გამოსვლების გამოქვეყნება დაიწყო ^_^
ჩემს გამოსვლას აქ გავაზიარებ, რომ ბლოგზე მქონდეს. მიყვარს ეს ფესტივალი და მართალია დღეების ნაცვლად ერთი თვე მოვუნდი პრეზენტაციის მომზადებას პატარას ხელში :))) მაგრამ ძალიან მინდოდა მონაწილეობის მიღება.

ესეც დემოს ლინკი გიტჰაბზე:
https://github.com/elatsoshvili/DevFestDemo2017

დამოუკიდებელი ტრანზაქციები

ტრანზაქციების ქვეშ ვგულისხმობ მონაცმებთა ბაზის ტრანზაქციებს, რომლებიც სხვადასხვა ოპერაციებს აერთიანებენ და ACID თვისებების დაცვით ბაზაში არსებული მონაცემების კორექტულობას უზრუნველყოფენ. ასეთი ტრანზაქციების მხარდაჭერა დღეს მგონი ყველა გავრცელებულ რელაციურ მონაცემთა ბაზას აქვს.

როდესაც ისეთი ოპერაციების ჩატარება გვინდა ბაზის მონაცემებზე, ან მრავალი ფუნქციის და პროცედურის გამოძახება, რომლებიც ერთმანეთზეა დამოკიდებული და თუ ერთგან მაინც რაიმე შეცდომა მოხდა, ყველაფერი უკან უნდა დაბრუნდეს, ყველა insert, update და delete უნდა გაუქმდეს, ასეთ შემთხვევაში ამ ყველაფერს ერთ ტრანზაქციაში მოვაქცევთ. მუშაობის დასრულების ბოლოს კი, იმის მიხედვით ყველაფერი წარმატებით შესრულდა თუ არა, ტრანზაქციას დავა-commit-ებთ, ან rollback ბრძანებით გავაუქმებთ. ამისთვის საჭიროა, რომ მთელი პროცესის განმავლობაში შეცდომის ჰენდლინგი სწორად გავაკეთოთ, მოვიფიქროთ სად დავიჭიროთ და დავამუშაოთ ხოლმე exception და სად გავუშვათ სულ გარეთ, მანამ სანამ სულ გარე ფუნქციაში არ გავა.

ანუ განვსაზღვროთ როდის უნდა დასრულდეს პროცესი  წარმატებით და როდის უნდა შეჩერდეს. შესაბამისად, ამ კონკრეტული პირობების შემთხვევაში ან სამუდამოდ შევინახავთ ცვლილებებს, ან გავაუქმებთ.

საქმე იმაშია, რომ ზოგჯერ გვჭირდება პროცესის შიგნით ზოგიერთი ნაწილი უპირობოდ დავაკომიტოთ, იმის მიუხედავად ტრანზაქცია წარმატებით დასრულდა თუ არა. მაგალითად, თუ გზადაგზა სხვადასხვა ფუნქციაში მომხდარ შეცდომებს ცხრილში ვინახავთ, გვინდა რომ ისინი ყველა შემთხვევაში შენახული იყოს. მაგრამ როდესაც მათ შესანახად კომიტს გავუშვებთ, ის იმ ტრანზაქციის ფარგლებში შესრულებულ ყველა ოპერაციას დააკომიტებს, ამიტომ სხვა გზა უნდა ვიპოვოთ.

თუ ასეთი სახის ამოცანა ორაკლში უნდა გადავჭრათ, მაშინ შეგვიძლია ზოგიერთი ტრანზაქცია მთავარი ტრანზაქციიდან დამოუკიდებლად გავუშვათ. ამისათვის ჩვენი ქვეპროგრამის declaration სექციაში (სადაც სხვადასხვა ცვლადების ან ქვეპროგრამების აღწერა ხდება), უნდა ჩავუწეროთ pragma autonomous_transaction;

pragma კომპილატორის დირექტივაა. მისი საშუალებით შეგვიძლია კომპილატორს ინფორმაცია მივაწოდოთ და ამით ქვეპროგრამის მუშაობის გზა შევცვალოთ. ორაკლში ასეთი კიდევ სამი სახის ინსტრუქცია არსებობს exception_init, restrict_references, serially_reusable, თუმცა მათ ახლა არ ჩავუღრმავდები.

მაგალითად, თუ ჩვენს log_error პროცედურაში (რომელიც ზევით არსებულ პროცესის ნაწილია და გზადაგზა შეცდომებს ინახავს ცხრილში) ჩავამატებთ autonomous_transaction პრაგმას, მასში გამოძახებული commit და rollback ბრძანებებს არ ექნება გავლენა მთავარ ტრანზაქციაზე და მხოლოდ ქვეპროგრამის ცვლილებებს შეინახავს. ცხადია, ეს დამოუკიდებლობა ვრცელდება მთავარ ტრანზაქციასთან დაკავშირებულ რესურსებსა და lock-ებზეც.

pragma autonomous_transaction-ით შეგვიძლია ანალოგიური შესაძლებლობა მივცეთ ორაკლის ტრიგერს, რომელშიც ჩვეულებრივ არ შეიძლება დაკომიტება, რადგან ის აღმძვრელ ოპერაციაზეა დამოკიდებული (ანუ რის გამოც მოხდა ტრიგერის გამოძახება). შესაბამისად, თუ ეს უკანასკნელი დაკომიტდა, ჩვეულებრივი ტრიგერიც დაკომიტდება და ასევე როლბექის შემთხვევაში.

დამოუკიდებელ ტრიგერში ამის გარდა შესაძლებელი გახდება DDL ბრძანებების შესულება (ცხრილის შექმნა, ცვლილება, ა.შ.).

MS SQL მონაცემთა ბაზაში, როგორც ჩანს, არ არსებობს ასეთი დამოუკიდებელი ტრანზაქციების ანალოგი (ყოველ შემთხვევაში ძველ ვერსიებში არ ჰქონდათ მისი მხარდაჭერა), მაგრამ არსებობს შემოვლითი გზები, რითიც ასეთი მიზნის მიღწევა შეიძლება.

MySQL-საც არ აქვს დამოუკიდებელი ტრანზაქციების ცნება, თუმცა აქ საქმე ცოტა სხვაგვარადაა. ზევით ნახსენებ ბაზებისგან განსხვავებით MySQL-ში საერთოდ ტრანზაქციების არსებობა მის storage engine-ებზეა დამოკიდებული. ასე რომ, თუ ვთქვათ შეცდომების ლოგირება გვინდა რომელიმე ცხრილში, შეგვიძლია იმ ცხრილისთვის MyISAM ძრავი გამოვიყენოთ რომელსაც არ აქვს ტრანზაქციების მხარდაჭერა, და როლბექის დროს ამ ცხრილში არსებულ ჩანაწერებს არაფერი მოუვა.

ბიტური ოპერაციები ორაკლში

დღეს ზედმეტი მოვინდომე და ბაზაში მონაცემების ბიტმასკების სახით შენახვა გადავწყვიტე, რომელიც მერე ისევ თვითონ ბაზას უნდა დაემუშავებინა. შემდეგ ვნახე, რომ ორაკლში არ არის რეალიზებული ბიტური ოპერაციები და არის მხოლოდ ერთი – bitand() ფუნქცია, რომელიც ბიტურ “and”-ს აკეთებს რიცხვებზე.
“or” და “xor”-ის მიღებაც ამ ფუნქციის გამოყენებით მარტივად შეიძლება. მაგალითად a და b რიცხვებზე “or” ოპერაცია გამოვა

a + b - bitand(a,b)

ხოლო “xor”-ს მივიღებთ თუ კიდევ ერთხელ გამოვაკლებთ:

a + b - bitand(a,b) - bitand(a,b)

არც ბიტური წაძვრები (shift >>) აქვს, მაგრამ მაგის ჩანაცვლება 2-ზე გამრავლებით და გაყოფით შეიძლება.

MySQL-ში არ დამჭირვებია, მაგრამ ეხლა შევხედე და ჰქონია პირდაპირ &, |, ~, ^, <<, >> და თან bit_count() ფუნქციაც, რომელიც ჩართულ ბიტების რაოდენობას აბრუნებს. მათი გამოყენება პირდაპირ sql ქვერებშიც შეიძლება.

ბოლოს დამაინტერესა და სხვა ბაზებსაც გადავწვდი. PostgreSQL-ში უკანასკნელის მსგავსად ყველა არის რეალიზებული, MS SQL-ში კი მხოლოდ and, or და xor არიან.

პრინციპში დიდი გამოყენება ალბათ არ აქვთ, მაგრამ ზოგ შემთხვევაში ძალიან მოსახერხებელი გამოდის. მაგალითად, თუ ობიექტთან დაკავშირებული სტატიკური (თუნდაც არასტატიკური) პატარა სიმრავლის შენახვაა საჭირო, ანუ ამ ობიექტისთვის სიმრავლეში რომელი ელემენტებია ”ჩართული” და რომელი არა.

Case sensitivity MySQL-ში

ორ ასოზე მეტის დაწერა ქართულად ვერ მოვახერხე.. 😀

ბოლო დროს ერთ პრობლემაზე იმდენი დრო დავკარგე, რომ დამამახსოვრდა. სინამდვილეში არაკორექტული ლოგირების გამო უფრო იყო, მაგრამ ეს არაა ახლა მთავარი. MySQL-ში როცა რამეს აღვწერთ, თითქმის ყველაფერი case-insensitive არის, ანუ არ აქვს მნიშვნელობა ველის სახელს დიდი ასოებით დავწერთ თუ პატარით.

კონკრეტულად, არ აქვს მნიშვნელობა ველის, ფუნქციის, პროცედურის, ინდექსის და ალიასის შემთხვევაში. ტრიგერი ამ მხრივ გამონაკლისია.

ხოლო რაც შეეხება ცხრილის და ბაზის სახელებს – მათი ქეის სენსითივობა ოპერაციულ სისტემაზეა დამოკიდებული.

კი, ვერ მივხვდი ლინუქსში რატომ აღარ მუშაობდა 😀

MySQL-ში მონაცემთა ბაზები დირექტორიებს შეესაბამება, ცხრილი კი მინიმუმ ერთ ფაილს ბაზის დირექტორიაში. ეს ნიშნავს რომ ვინდოუსის შემთხვევაში რეგისტრს მნიშვნელობა არ აქვს, იუნიქსის პლატფორმაზე კი აქვს.

შეიძლება lower_case_table_names პარამეტრის გამოყენებაც, თუმცა ამას ყველაფერი პატარა რეგისტრში გადაჰყავს და თუ მიგრაციას აკეთებთ, მაშინ არსებული ცხრილების და ბაზების სახელების კონვერტაცია იქნება საჭირო წინასწარ.