[C++]zmiana sposobu kodwania

0

user image

W C++ potrafię trochę pisać więc mój problem nie jest natury implementacyjnej. Napisałem programik(kod niżej) który wyświetla kodowanie znaczka w formie dziesiętnej, binarnej i hexadycemalnej. Zdaje sobie też sprawę że np literkę "ś" w ASCII nie zakoduję (chyba że o czymś nie wiem :-) ). Nie wiem jak zrobić, żeby np wczytując literkę "ą" która ma kod dziesiętny 185(standard win 1250) potem zamieniając ją na kod unicode dziesiętnie 261 i potem zapisując ten znak do pliku to żeby to pozostała literka "ą" a nie jakiś krzaczek :-| Wiem o konieczności używania wchar itp ale nie wiem w którym miejscu kodu mogłoby mi się to ustrojstwo przydać.
Jeżeli napisałem jaką totalną głupotę to nie śmiejcie się ze mnie zbyt głośno :d Szperałem sporo w sieci i jestem lekko zdezorientowany(być może nie potrafię szukać).

A to mój obiecany kodzik, pokazuje znaki które można zapisać na jednym bajcie ale to spokojnie zupgraduje jeśli będzie konieczność:
EDIT: broń Boże niech nikt kodu nie analizuje, nie w tym jest problem. Mam problem natury merytorycznej a ten kodzik napisałem nie wiedząc dokładnie jeszcze co mam robić a pomyślałem że może się przydać przy wczytywaniu plików ;-P

#include <cstdlib>
#include <iostream>
#include <fstream>

using namespace std;

void binarny(int liczba, int *wsk_binarny);

int main(int argc, char *argv[])
{
    ifstream::pos_type rozmiar;
    ifstream plik("tekst.txt",   ios::in|ios::ate);
    
    if(plik.is_open())
    {
     rozmiar=plik.tellg();
     plik.seekg (0,ios::beg);
     
     int rozmiar2=(int)rozmiar;
     char *tab_char;
     tab_char=new char [rozmiar2];        
     char *wsk_tab=tab_char;
          
     plik.read(wsk_tab, rozmiar2);
     
     int *tab_int;
     tab_int=new int [rozmiar2];
     int tab_binar[8];
     int liczba;
     
     for(int i=0;i<rozmiar;i++)
     {
            cout<<tab_char[i]<<"\t";
            tab_int[i]=static_cast<int>(tab_char[i]);
            if(tab_int[i]<0){
            tab_int[i]+=256;}
            liczba=tab_int[i];
            binarny(liczba, tab_binar);
            cout<<tab_int[i]<<"\t";
            for(int i=0;i<8;i++)
            {
            cout<<tab_binar[i];
            }
            
            int prawa_hex[4], lewa_hex[4];
            for(int i=0;i<4;i++)
            {
                    lewa_hex[i]=tab_binar[i];
            }
            int m=0;
            for(int i=4;i<8;i++)
            {
                    prawa_hex[m]=tab_binar[i];
                    m++;           
            }
            int tab_mnozenia[4]={8,4,2,1};
    
            for(int i=0;i<4;i++)
            {
                    lewa_hex[i]=lewa_hex[i]*tab_mnozenia[i];
                    prawa_hex[i]=prawa_hex[i]*tab_mnozenia[i];          
            }
    
            int lewa_hexF=lewa_hex[0]+lewa_hex[1]+lewa_hex[2]+lewa_hex[3];
            int prawa_hexF=prawa_hex[0]+prawa_hex[1]+prawa_hex[2]+prawa_hex[3];
            
            char znak[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
            char wartosc_hex[2];
            for(int i=0;i<16;i++)
            {
                    znak[i];
                    if(lewa_hexF==i)
                    {
                      wartosc_hex[0]=znak[i];
                    }
                    if (prawa_hexF==i)
                    {
                      wartosc_hex[1]=znak[i];
                    }
             }
             
            cout<<"\t";
            cout<<wartosc_hex[0]<<wartosc_hex[1];
            cout<<endl;
     }     
}

else
{
    cout<<"kurwa nie otworzyl sie skubaniec"<<endl;
}
   
  plik.close();   
    system("PAUSE");
    return EXIT_SUCCESS;
}

void binarny(int liczba, int *wsk_binarny)
{
     int reszta;
     int tab_pom[8];
     for(int i=0;i<8;i++)
     {
             reszta=liczba%2;
             if(reszta!=0){
             tab_pom[i]=1;}
             else{
             tab_pom[i]=0;}
             liczba=liczba/2;
     }
     int p=7;
     for(int i=0;i<8;i++)
     {
             wsk_binarny[i]=tab_pom[p];
             p--;
     }
}
0

Może nieuważnie patrzyłem ale ja tu nie widzę zapisu do pliku (?) Anyway, zapis do UTF-16 jest nieco trudniejszy niż by się wydawało, a jeszcze bardziej zapis do UTF-8 - nie wystarczy zamienić liczby 8-bitowej na 16-bitową. Proponuję o tym poczytać jeżeli naprawdę chce Ci się w to bawić. Jeżeli już znajdziesz poprawny sposób zamiany tekstu, to mogę poradzić przepisanie go do tablicy charów i zapisać (binarnie) za pomocą ostream::write().

0

Dobrze patrzyłeś, zapisu nie ma. To tylko wczytywanie i przedstawienie znaku w postaci dec, bin i hex. Badając otchłanie internetu rozświetlając sobie drogę googlem i różnych dokumentacji zauważyłem że temat nie jest łatwy, przynajmniej biorąc pod uwagę moje obecne umiejętności. Ech, widzę że nikogo tutaj nie bardzo to interesuje więc muszę sobie poradzić sam :) Anyway dzięki za nie skasowanie tematu :)

EDIT: Teraz kombinuje jak używając składni C++ mogę rozpoznować preambuły na początku pliku mówiące o rodzaju kodowania, a nuż mi się uda xD

0

zakrec sie kolo strumieni 'wide' - wcin, wcout, wstring, ifwstream, oraz imbue, locale..

@powyzsze zas:
a imć prowadzacy mowi o jakim U-unicode? 32 czy 16? UTF16 rozpoznasz latwo, pliki zaczynaja sie od 0xFFFE lub 0xFEFF, zaleznie od endianess. tzn. zazwyczaj sie zaczynaja..

0

Chodzi mu zapewne o UTF-16 LE, jak pliki się zaczynają to akurat wiem:

FF FE - UTF-16 Little Endian
FE FF - UTF-16 Big Endian
FF FE 00 00 - UTF-32 Little Endian
00 00 FE FF - UTF-32 Big Endian
EF BB BF - UTF-8

Teraz myślę jak je odczytać. Dziękuję za cenne wskazówki dotyczące strumieni.

ps. Damn it....tylko nie locale xD

0

utf16 plus widestream z tego co pamietam mialy jeden feler, i trzeba bylo dopisac maly kawalek kodu zajmujacy sie odcinaniem/doklejaniem preambuly - jest do znalezienia w sieci - ale poza tym, dzialaly bardzo ladnie..

a jak je odczytac? ot, po prostu, binarnie, peek/get/read, zadne tekstowe oper>>, przynajmniej poki imbue z wlasciwym locale nie zrobisz

0

Sprawdzić kilka pierwszych bajtów pliku to pikuś. Pliki utf-8 mogą być zapisane bez preambuły. W takim wypadku, jeśli tekst wygląda na ascii trzeba by go sprawdzić, czy nie jest zgodny z utf-8. Trzeba sprawdzić wszystkie znaki >127 i co za nimi stoi, tu masz opisane reguły:
http://www.instructables.com/id/SYGL47RFDYPTCVC/

0

prawda to. ale, AFAIK, do celow 'zadania domowego' mozna zalozyc ze plik bez preambuly=plik bledny. co innego w 'prawdziwym swiecie' :)

0

Nie mniej jednak bardzo prosto można to zaimplementować. Jeśli plik nie posiada preambuły przyjmujemy że to utf-8, przy pierwszej niezgodności podczas dekodowania dekodujemy jeszcze raz tym razem w trybie Windows 1250. Dekodowanie wystarczy powtórzyć od pierwszego znaku nie-ASCII.

0
quetzalcoatl napisał(a)

.

a jak je odczytac? ot, po prostu, binarnie, peek/get/read

A ja kombinowałem jak koń pod górę z jakimiś dziwadłami :d Napisałem już całą funkcję zwracającą informację o kodowaniu. Co prawda nie jest ona super prawidłowa bo założyłem w niej że jeśli przed tekstem nie ma BOMu to tekst jest kodowany jako ASCII (Notatnik domyślnie koduje tekst jako ASCII). Wiem że .xml są kodowane UNICODEm więc tutaj moja funkcja by mnie okłamała ;] Ale że jest to tylko projekt dla profesorka to nie zamierzam się wysilać nadto, i tak będzie używał notatnika do sprawdzania programu.
Teraz zabieram się za konwersję i powoli coś się wykluwa, jeszcze raz dziękuję za wskazówki.

0

Noi mam problem, od kilku godzin(tak wiem żałosne)męczę kodzik do przekonwertowania ASCII na UNICODE. Użyłem funkcji MultiByteToWideChar() ale coś nie do końca mi dobrze dziala, jeśli wierzyć tablicy kodów na wiki to polskie znaki koduje dobrze ale nie chce ich zapisać do pliku :-| Tworzy strumień ale nie wysyła znaków, tak jakby coś z writem było nie tak. Znaki łacińskie za to wysyła :-| O to kod, wstawiłem parę komentarzy dla większej jasności:

else if(kodowanie==ANSI)
	{
		ifstream::pos_type rozmiar;
		ifstream plik("ASCII.txt", ios::in|ios::binary|ios::ate);
		rozmiar=plik.tellg();//do zmiennej rozmiar podstawiamy informację o ilosci bajtow pliku wejsciowego ANSI
		plik.seekg(0, ios::beg);

		//int arozmiar=(int)rozmiar;		
		char *tekst;
		tekst=new char[rozmiar];

		wchar_t *wtekst;
		wtekst=new wchar_t[2*rozmiar];//UNICODE zapisuje znaki na dwóch bajtach więc zainicjowana tablica musi być 2x większa niz wejsciowa

		wchar_t *wtekst_final;
		wtekst_final=new wchar_t[2*rozmiar+4];
		//wtekst_final[0]='F';wtekst_final[1]='F';wtekst_final[2]='F';wtekst_final[3]='E'; wide array z wstawioną preambula UNICODE na początku, na przyszlosc :)

		plik.read(tekst, rozmiar);//czytanie
		plik.close();//zamkniecie strumienia

		int *atekst;//kilka linijek kodu by poznac kody znakow wejsciowych
		atekst=new int[rozmiar];
		for(int i=0;i<rozmiar;i++){
				atekst[i]=static_cast<int>(tekst[i]);
				if(atekst[i]<0){
					atekst[i]+=256;}
				cout<<atekst[i]<<"  ";}


		MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, tekst,rozmiar, wtekst, 2*rozmiar);//konwersja ASCII->UNICODE

		
		/*int x=0;
		for(int i=4;i<2*rozmiar+4;i++)
		{
			wtekst_final[i]=wtekst[x];
			x++;
		}*/

		wofstream plik_zapis("ASCII_TO_UNI.txt", ios::out);//wide strumien do zapisu
		plik_zapis.write(wtekst, 2*rozmiar);//zapis moj nieszczesny
		plik_zapis.close();

		cout<<endl<<"Przekonwertowalem, zapisalem"<<endl;
		cout<<endl;
		for(int i=0;i<2*rozmiar;i++)//do wywswietlania
			wcout<<wtekst[i]<<"  ";//w tym miejscu jak wstawimy zwykly strumien 'cout' to pokaza nam sie kody dzisietne znakow
	}
0
  1. Zapisz wynik binarnie.
  2. Cieknie pamięć (tekst, wtekst, atekst)
  3. <quote> for(int i=0;i<rozmiar;i++){
    atekst[i]=static_cast<int>(tekst[i]);
    if(atekst[i]<0){
    atekst[i]+=256;}
    cout<<atekst[i]<<" ";}</quote>Uh. Wystarczy rzutować:
for(int i=0;i<rozmiar;i++) cout << (int)(unsigned char)tekst[i];
  1. Znaków nie będzie "2*rozmiar", tylko około "rozmiar". Dokładną ilość zwraca MultiByteToWideChar.
  2. Wszystko masz wewnątrz klamry "if(kodowanie==ANSI)", czyli wszystko musisz mieć powtórzone dla każdego kodowania. Nie dobrze. Tylko niektóre elementy będą charakterystyczne dla danego kodowania. Napisz jeden kod dla każdego z nich.
    Schemat powinien być taki:

kodowanie_zrodlowe = rozpoznaj(plik);
oryginalny = wczytaj(plik); - wczytujemy binarnie wszystkie bajty bez preambuły
zmeiniony = konwertuj(kodowanie_zrodlowe, kodowanie_docelowe, zdekodowany, dlugosc_zdekodowanego);
zapisz(kodowanie_docelowe, zmeiniony, dlugosc_zmeinionego); - zapisuje odpowiednia preambule i zakodowane bajty

Do wszystkiego używaj strumieni binarnych.

0
adf88 napisał(a)
  1. Zapisz wynik binarnie.

Czyli wynik konwersji MultiByteToWideChar mam zamienić na binarke i surowe bity wysyłać writem? Problemu z przejściem na bin nie będzie ale nigdy nie wysyłałem surowych bitów, tylko znaczki. No, chyba że czegoś nie zrozumiałem? :>

adf88 napisał(a)

Do wszystkiego używaj strumieni binarnych.

Dodałem do strumienia wyjściowego(sam nie wiem jak mogłem o tym zapomnieć) tryb ios::binary i bez wykonania pierwszego kroku nie zadziałało.

EDIT: Co do reszty masz absolutną rację, na swoje usprawiedliwienie powiem tylko ze najpierw chciałem żeby to zadziałało a potem zabrać się za optymalizację.

0

plik otwarty w trybie binarnym -- po prostu wprost czytasz tresc i zapisujesz. tak jakbys sobie wyobrazal operacje na pliku
plik otwarty w trybie tektowym -- moga dziac sie magie. system moze interpretowac i konwertowac znaki specjalne na inne. nie chodzi tu o kodowanie! raczej o np. \r <-> \r\n <-> \n
tak wiec nie boj sie binarnego, boj sie tekstowego :)

0

Chyba źle sformułowałem pytanie, albo niezbyt jasno. Chodzi o to że to cudo:

wofstream plik_zapis("ASCII_TO_UNI.txt", ios::out|ios::binary);
plik_zapis.write(wtekst, rozmiar);

...nie działa prawidłowo, mimo ze strumień ustawiony jest na tryb binarny. Po pierwsze nie wysyła polskich znaków mimo dobrej konwersji, oraz zapisany plik jest ustawiany jako ANSI mimo że dodaje na początek preambułę dla UTF16 LE. Nie boje się trybu binarnego, piszę za krótko żeby mieć jakieś zakorzenione przyzwyczajenia ;-)

Po wypowiedzi adf88 sądziłem że mam moją tablicę przekonwertowanych znaków wcha_t zamienić na tablicę bool, ale szybkie kuknięcie w MSDN:
basic_ostream& write(const char_type *_Str,streamsize _Count);
wyprowadziło mnie z tego błędu.

ps. Podziwiam waszą cierpliwość do mnie :d


EDIT: Jako że inne projekty się nagrały musiałem ten odstawić, ostatnio wróciłem jednak do problemu i nawet udało mi się go rozwiązać. W przypadku konwersji UNICODE na ASCII lub odwrotnie trzeba użyć przy zapisie i zczytywaniu jeszcze funkcji open(). Wygląda to mniej więcej tak:

ofstream plik_zapis;
plik_zapis.open("ASCII_TO_UNI.txt", ios::out|ios::binary);
plik_zapis.write((char*)wtekst, size*sizeof(wchar_t));
plik_zapis.close();

Może się przyda komuś ;-) Teraz męczę UTF8 ;]

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