Strona 1 z 4

Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: wtorek, 5 stycznia 2010, 17:32
przez mko000
Witam. Otóż znalazłem problem w powyższej poradzie. Oto kod:
Kod: Zaznacz cały
// Plik źródłowy np. Unit1.cpp.
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int iFileHandle;
int iFileLength;
int iBytesRead;
int iBytesWrite = 0;
char *pszBuffer;

if(OpenDialog1->Execute())
{
  try
  {
   iFileHandle = FileOpen(OpenDialog1->FileName, fmOpenRead);
   iFileLength = FileSeek(iFileHandle, 0, 2);
   FileSeek(iFileHandle, 0, 0);
   pszBuffer = new char[iFileLength+1];
   iBytesRead = FileRead(iFileHandle, pszBuffer, iFileLength);
   FileClose(iFileHandle);

   while(iBytesWrite < iBytesRead)
   {
    if(pszBuffer[iBytesWrite] == 0)
    pszBuffer[iBytesWrite] = ' ';
    iBytesWrite++;
   }

   Memo1->Lines->SetText(pszBuffer);
   delete [] pszBuffer;
  }
  catch(...)
  {
   Application->MessageBox("Can't perform one of the following file operations: Open, Seek, Read, Close.", "File Error", IDOK);
  }
}
}
//--------------------------------

Na formie mam tylko memo1 i opendialog1

a błąd dotyczy tej linijki
Memo1->Lines->SetText(pszBuffer);

[BCC32 Error] Unit3.cpp(43): E2034 Cannot convert 'char *' to 'wchar_t *'
[BCC32 Error] Unit3.cpp(43): E2342 Type mismatch in parameter 'Text' (wanted 'wchar_t *', got 'char *')

jak zamienic ta zmienna ?

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: wtorek, 5 stycznia 2010, 18:56
przez Witold
spróbuj: Memo1->Text = pszBuffer;

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: wtorek, 5 stycznia 2010, 19:01
przez Cyfrowy Baron
Porada była pisana dla środowiska C++Builder do wersji 2007. Problem polega na tym, że w nowszych wersjach C++Builder od 2009 domyślnie pracuje na zmiennych typu UnicodeString. Musisz zmienić typ char na wchar_t, czyli tutaj:

Kod: Zaznacz cały
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int iFileHandle;
int iFileLength;
int iBytesRead;
int iBytesWrite = 0;
wchar_t *pszBuffer;

if(OpenDialog1->Execute())
{
  try
  {
   iFileHandle = FileOpen(OpenDialog1->FileName, fmOpenRead);
   iFileLength = FileSeek(iFileHandle, 0, 2);
   FileSeek(iFileHandle, 0, 0);
   pszBuffer = new wchar_t[iFileLength+1];
   iBytesRead = FileRead(iFileHandle, pszBuffer, iFileLength);
   FileClose(iFileHandle);

   while(iBytesWrite < iBytesRead)
   {
     if(pszBuffer[iBytesWrite] == 0)
     pszBuffer[iBytesWrite] = ' ';
     iBytesWrite++;
   }

   Memo1->Lines->SetText(pszBuffer);
   delete [] pszBuffer;
   }
   catch(...)
   {
     Application->MessageBox(L"Can't perform one of the following file operations: Open, Seek, Read, Close.", L"File Error", IDOK);
   }
}
}

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: wtorek, 5 stycznia 2010, 21:24
przez Witold
Cyfrowy Baron napisał(a):...
Musisz zmienić typ char na wchar_t, czyli tutaj:
...


Zdaje mi się że to co napisałeś zmieni interpretacje znaków char->wchar_t (pliki binarne nie są chyba zapisywane w unicode) i dodasz trochę danych od siebie (albo nie)?

Z ciekawości, czemu służyła ta zmiana kolejności postów ?

Zdaje mi się też że Kinio zapomniał pszBuffer[iFileLength] = 0; a FileOpen, FileSeek, FileRead, FileClose nie rzucają wyjątków.

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: wtorek, 5 stycznia 2010, 21:47
przez Cyfrowy Baron
Co proponujesz zamiast wchar_t?

Przesunąłem wątek, gdyż twoja odpowiedź nie rozwiązywała problemu.

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: wtorek, 5 stycznia 2010, 23:52
przez Witold
Cyfrowy Baron napisał(a):Co proponujesz zamiast wchar_t?


dalej: Memo1->Text = pszBuffer;

UnicodeString ma konstruktor: System::UnicodeString * __fastcall UnicodeString(const char * src);
http://docwiki.embarcadero.com/VCL/en/S ... nstructors

Cyfrowy Baron napisał(a):Przesunąłem wątek, gdyż twoja odpowiedź nie rozwiązywała problemu.


mko000 napisał(a):a błąd dotyczy tej linijki
Memo1->Lines->SetText(pszBuffer);

[BCC32 Error] Unit3.cpp(43): E2034 Cannot convert 'char *' to 'wchar_t *'
[BCC32 Error] Unit3.cpp(43): E2342 Type mismatch in parameter 'Text' (wanted 'wchar_t *', got 'char *')


Znaczy się po zmiane:
Kod: Zaznacz cały
  Memo1->Lines->SetText(pszBuffer);

na
Kod: Zaznacz cały
Memo1->Text = pszBuffer;

błąd „Cannot convert 'char *' to 'wchar_t *'” pojawia się dalej ? Może pojawia się jakiś inny błąd ?

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: środa, 6 stycznia 2010, 12:42
przez polymorphism
Witold napisał(a):Zdaje mi się że to co napisałeś zmieni interpretacje znaków char->wchar_t (pliki binarne nie są chyba zapisywane w unicode) [...]

Dokładnie. Taka zwykła zamiana typów jest błędna, bo w unikodzie, przy kodowaniu utf-16, nie ma takiej dowolności, jeśli chodzi kody znaków, jak w kodowaniu ansi, gdzie (prawie) każda wartość 'coś' znaczy. W unikodzie to nie przejdzie, niektóre kody po prostu nie istnieją.

UnicodeString ma konstruktor: System::UnicodeString * __fastcall UnicodeString(const char * src);

Tylko żeby przekonwertować ciąg bajtów na unikod, musisz znać jego kodowanie. Mowa o danych binarnych, więc trudno mówić o konkretnym kodowaniu, a co za tym idzie o poprawnej konwersji.

Kod: Zaznacz cały
pszBuffer = new wchar_t[iFileLength+1];

iFileLength to długość pliku w bajtach, więc pszBuffer jest dwa razy większy niż powinien (druga połowa bufora będzie zawierać przypadkowe wartości). Na jeden znak przypadają 2 bajty.

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: środa, 6 stycznia 2010, 12:57
przez Cyfrowy Baron
Kod: Zaznacz cały
Memo1->Text = pszBuffer;


To rzeczywiście działa, są jednak dwa ale, wczytanie pliku o rozmiarze 2 MB trwa około minuty, wczytana dane binarne są nieprawidłowo, zawierają mnóstwo pustych spacji.

Text = UnicodeString;
SetText(wchar_t *);

To mnie trochę zmyliło, sądziłem, że SetText(UnicodeString), a konwersja dokonuje się niejawnie.

Pytanie jak dokonać konwersji char na wchar_t.

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: środa, 6 stycznia 2010, 15:09
przez polymorphism
wczytana dane binarne są nieprawidłowo, zawierają mnóstwo pustych spacji.

Bo dane binarne to nie zawsze są tekstem. Takich danych nie podaje w ten sposób kontrolkom stricte tekstowym.

Pytanie jak dokonać konwersji char na wchar_t.


KOD cpp:     UKRYJ  
locale loc(".1250");// <--- przykładowe kodowanie

const ctype<wchar_t> &ct = use_facet<ctype<wchar_t> >(loc);

/* dla każdego bajta */
...
char chr = /* whatever */
wchar_t wc = ct.widen(chr);
bool ok = ct.is(ctype<wchar_t>::alpha | ctype<wchar_t>::digit | ctype<wchar_t>::punct,wc);
if(!ok) wc = 0x2588; // jeśli kod nie jest literą, cyfrą lub znakiem interpunkcyjnym, to wtedy zmiana na █
 

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: środa, 6 stycznia 2010, 15:22
przez Cyfrowy Baron
Bo dane binarne to nie zawsze są tekstem. Takich danych nie podaje w ten sposób kontrolkom stricte tekstowym.


Nie o to mi chodziło. Ten sam kod użyty w C++Builder 2007 w miejscu spacji wstawia kwadraciki i działa szybciej, plik jest wczytywany niemal natychmiast.

Co do szybkości działania, to sądziłem, że opóźnienie wywołuje pętla, ale tak nie jest, co można sprawdzić za pomocą tego kodu:

Kod: Zaznacz cały
int iFileHandle;
int iFileLength;
int iBytesRead;
int iBytesWrite = 0;
char *pszBuffer;
AnsiString dane;

if(OpenDialog1->Execute())
{
  try
  {
   iFileHandle = FileOpen(OpenDialog1->FileName, fmOpenRead);
   iFileLength = FileSeek(iFileHandle, 0, 2);
   FileSeek(iFileHandle, 0, 0);
   pszBuffer = new char[iFileLength+1];
   iBytesRead = FileRead(iFileHandle, pszBuffer, iFileLength);
   FileClose(iFileHandle);

   while(iBytesWrite < iBytesRead)
   {
     if(pszBuffer[iBytesWrite] == 0)
     pszBuffer[iBytesWrite] = ' ';
     iBytesWrite++;
   }
   dane = pszBuffer;
   delete [] pszBuffer;
   }
   catch(...)
   {
     Application->MessageBox(L"Can't perform one of the following file operations: Open, Seek, Read, Close.", L"File Error", MB_OK);
   }
}
  ShowMessage("Przepisywanie");
Memo1->Text = dane;


Opóźnienie powstaje przy przepisywanie danych do obiektu Memo. Jak więc sądzę problemem jest przekonwertowanie danych na UnicodeString.

Jak widać użyłem zmiennej typu AnsiString do przechowania zawartości bufora, to oczywiście nic nie daje, gdyż opóźnienie nadal występuje.
Kolejne pytanie jak szybko przepisać wartość AnsiString do UnicodeString bez opóźnienia?

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: środa, 6 stycznia 2010, 15:44
przez polymorphism
Kod który podałem, po drobnych zmianach, z użyciem wstringa, 2MB bufor konwertuje mi w ~0.6 sekundy (2x2.5GHz), a 10MB w ~2.8 sekundy. Więc sama konwersja nie powinna być problemem.

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: środa, 6 stycznia 2010, 18:20
przez Witold
polymorphism napisał(a):
UnicodeString ma konstruktor: System::UnicodeString * __fastcall UnicodeString(const char * src);

Tylko żeby przekonwertować ciąg bajtów na unikod, musisz znać jego kodowanie. Mowa o danych binarnych, więc trudno mówić o konkretnym kodowaniu, a co za tym idzie o poprawnej konwersji.


Pomysł takiego przerzucenia danych z pliku do TMemo nie jest mój, próbowałem tylko wyeliminować wymieniony błąd. Zdaje mi się że znaki w TMemo wg tego sposobu (+poprawka) będą takie same dla środowiska z AnsiString i UnicodeString (wszystko wg kodowania systemowego). Twoje rozwiązanie z analizą znaków jest MSZ lepsze.

Cyfrowy Baron napisał(a):Kolejne pytanie jak szybko przepisać wartość AnsiString do UnicodeString bez opóźnienia?


Trzeba by mieć pewność, że to jest problemem. Może chodzi o WordWrap.

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: czwartek, 7 stycznia 2010, 12:02
przez Cyfrowy Baron
Witold trochę mi namieszał.
kinio nie bez powodu użył funkcji SetText(char *) gdyż ta funkcja pobiera wartości typu char, podczas gdy Memo->Text pobiera wartości typu AnsiString (do wersji BCB 2007)
Dlatego też, jeżeli chcemy mieć prawidłowo wyświetlane znaki to należy używać funkcji SetText.

Żadne z rozwiązań, które testowałem nie daj zadowalających rezultatów. Kod po prostu działa za długo. Co do kodu polymorphis to otrzymuję kilka błędów i nie mogę go przetestować.

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: czwartek, 7 stycznia 2010, 12:21
przez polymorphism
Jakich błędów?

Re: Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu k

Nowy postNapisane: czwartek, 7 stycznia 2010, 12:39
przez Cyfrowy Baron
Jest problem z tą linią:

Kod: Zaznacz cały
wchar_t wc = ct.widen(chr);



[BCC32 Error] Unit1.cpp(149): E2285 Could not find a match for 'ctype<wchar_t>::widen(char *)'



Potem gdy próbuję przepisać zawartość zmiennej wc do Memo poprzez funkcję SetText otrzymuję komunikat:


[BCC32 Error] Unit1.cpp(153): E2342 Type mismatch in parameter 'Text' (wanted 'wchar_t *', got 'wchar_t')



a tak wogóle to nie bardzo rozumiem ten twój kod.