Odczyt z bazy danych - problem z kodowaniem znaków

0

Mam gotową bazę danych SQLite, nie mogę jej modyfikować (nawet nie potrzebuję, tylko zwracam uwagę, jeśli ktoś sugerowałby zapisanie jej na nowo przez jakąś zewnętrzną przeglądarkę/edytor baz SQLite), próbuję ją odczytać przez providera System.Data.SQLite (http://sqlite.phxsoftware.com/), ale pojawił się problem z polami VARCHAR, gdzie są teksty zapisane m.in. cyrylicą. Przypuszczalnie oryginalny program zapisał to jakoś po swojemu i przy odczycie jakichkolwiek danych C# traktuje to jako string, czyta jako Unicode i traci dane.

Jak robię odczyt?

  • otwieram połączenie (próbowałem z UseUtf16Encoding=true ale nie robi żadnej różnicy)
  • przypisuję SQLiteDataAdapter, DataSet, DataTable
  • puszczam zapytanie po SQLiteDataAdapter
  • z DataRow wyciągam dane i wrzucam do ListView (DataGridView jest brzydki)

Dane są utracone już na etapie wyciągania z DataRow, więc żadne konwersje nie pomogą. Jednocześnie w bazie danych znaki są, oryginalna aplikacja je odczytuje, SQLite Administrator też (ale są inne przeglądarki baz danych SQLite, które sobie z tym nie radzą).

Czy da się zmusić odczyt pola VARCHAR nie jako object (który jest na zapewne bazie typu pola rozpoznawany jako string), tylko jako przykładowo byte[]? Żeby odzyskać 1:1 to co jest zapisane? Później przejście z byte[] do string z zachowaniem kodowania znaków to byłby banał, ale jak się dobrać do tego?

Wszelkie pomysły mile widziane :) Z góry dzięki :)

PS: Halp - I are stuk ;)
user image

0

Próbowałeś zmianiać wartość właściwości SQLiteConnection.UTF16 http://www.devart.com/dotconnect/sqlite/docs/Devart.Data.SQLite~Devart.Data.SQLite.SQLiteConnection~UTF16.html
Albo zamiast adaptera użyj readera http://www.devart.com/dotconnect/sqlite/docs/Devart.Data.SQLite~Devart.Data.SQLite.SQLiteDataReader_members.html i spróbuj użyć metod GetString lub GetBytes (i użyć odpowiedniego encoding).

0

Dzięki za odpowiedź, GetBytes to by było idealne, ale niestety - DevArt DotConnect jest płatny i to nie mało :( Szkoda :(

UTF16 odpalam u siebie przez UseUtf16Encoding=true przy łączeniu, niezależnie, czy ustawiam na True, czy na False - niestety mam same "?" na ekranie. Gdyby odczytało w jakikolwiek sposób chociaż różne znaki, nawet w niepoprawnym kodowaniu, to da się to odkręcić, ale gdy mam same "?", to już nic nie mogę z tego zrobić :(

Rozglądam się teraz za innymi providerami dla SQLite, ale trochę będę miał przerabiania, a dodatkowo nie mam pewności, czy zmiana pomoże, więc jakby ktoś miał jeszcze pomysł, to proszę śmiało wrzucać, nawet najbardziej krejzi szalone koncepcje :)

1

Za ten komentarz to sam muszę się opier***.
Co z tego że używasz innego providera, implementacja każdego readera wywodzi się z klasy abstrakcyjnej DbDataReader, dlatego każdy reader ma implementację GetString i GetBytes!
Provider, którego używasz, z tego co przejrzałem jego kod, jeśli w connection string użyjesz UseUTF16Encoding=true to używa kodowania utf16, inaczej używa utf8.
Jeśli to nie pomoże to daj znać, to podeśle ci na prv mój email i podeślesz mi kawałek twojej bazki do testów.

0

Dzięki za odpowiedź, to mi podsunęło inną koncepcję odczytu bazy danych, ale niestety i tak niepowodzenie.

Okazało się, że ReadBytes, jeśli kolumna jest typem VARCHAR zwraca Invalid Cast Exception (w tym samym czasie ReadString i ReadChars zwracają dane, ale ze złym kodowaniem, niezależnie czy UTF16 jest aktywny, czy nie).

Zbudowałem zapytanie na takiej zasadzie (zapożyczone z forum autora providera SQLite):

                            using (SQLiteCommand com = new SQLiteCommand())
                            {
                                com.CommandText = "SELECT servicename FROM channels WHERE ckey = 1;"; // na potrzeby testów wstawiłem pierwszy element z zapytania
                                com.Connection = cnn;

                                using (SQLiteDataReader rd = com.ExecuteReader())
                                {
                                    rd.Read();

                                    // MessageBox.Show(rd.GetFieldType(0).ToString()); //typ danych

                                    long length = rd.GetBytes(0, 0, null, 0, 0); // InvalidCastException
                                    byte[] myBinaryData = new byte[length];
                                    rd.GetBytes(0, 0, myBinaryData, 0, MyBinaryData.Length);
                                }
                            }

Ponieważ baza nie zawiera niczego "top secret", to zapodaję: http://www25.zippyshare.com/v/21332828/file.html

Challenge: odczytać servicename z tabeli channels z bazy dla ckey 115. Powinno być: "ŇÄĘ" (tak wskazuje oryginalny program oraz SQL Administrator, to niby nie jest cyrylica, ale tak naprawdę to jest, tylko w Windows codepage 1251 - "ТДК" ) :P Cały trick, to wydobyć trzy różne znaki, inne od "?" - jak to się uda, to później konwersja z jednej strony kodowej do innej to bajka...

PS: Jeśli się nie da, to proszę o info, że się nie da, żebym nad tym nie tracił więcej czasu - z góry dzięki ;)

1
BDA DVB napisał(a)

Cały trick, to wydobyć trzy różne znaki, inne od "?" - jak to się uda, to później konwersja z jednej strony kodowej do innej to bajka...

Tzn. zapytanie z bazy wyciąga Ci trzy znaki zapytania? W jakim kodowaniu?

0

Tak, dla wspomnianego "challenge" wyżej dokładnie coś takiego:
user image
Z kodowania w SQLite mogę jedynie albo skorzystać z UTF8, albo przy połączeniu do bazy wymusić UTF16, niestety nie zmienia to wyniku. Później jakakolwiek obróbka tych znaków zapytania już nie ma sensu (gdy skorzystałem z ReadChars - dostałem trzy razy znak 65533 czyli takie znaki zapytania jak na obrazku powyżej, z tego już nic nie mogę zrobić).

0

Znalazłem sam rozwiązanie - szukałem po złej stronie. Kombinowanie w C# nie ma żadnego sensu - trzeba działać w momencie odczytu, wykonując odpowiednie zapytanie. Przeglądając dokumentację SQLite trafiłem na informację o funkcji hex(x) zwracającej string, który zawiera reprezentację ciągu jako wartości szesnastkowe poszczególnych znaków. Wystarczyło dodać do zapytania na samym początku SELECT *, hex(servicename) FROM... a później zrzucić uzyskany string do byte[] i voila - mocno kombinowane, ale działa :)

Dzięki za pomoc, jeśli macie jakieś dodatkowe pomysły, proszę śmiało się dzielić ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1