Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Ihanat merkistömme

Sivun loppuun

FrozenFire [24.06.2007 10:35:22]

#

Täällä aina hetkittäin keskustellaan siitä miten windowsin konsolissa saa ääkköset näkymään. No mulla on kutakuinki sama ongelma mutta siihen tuo lisää monimutkaisuutta se että ohjelman on tarkoitus kääntyä myös linuxilla jatkossakin. Joten järjestelmällinen kirjainten korvaus suoraan koodiin ei nyt oikein toimi.

Lisäksi ohjelma käyttää tekstitiedostoja, jotka pitäisi edelleen näkyä oikein. Ohjelmaan olen nyt sovittamassa samalla ncurses/pdcurses käyttöliittymää. Miten nämä merkit kannattaisi konvertoida, pitäisikö kasata oma funktio vai löytyykö hienostuneempia työkaluja?

Lisäksi nuo numeroarvot ovat ilmeisesti hieman erit kuin yleensä nuille ä:lle (E4/228) ja ö:lle(F6/246)

Pekka Karjalainen [24.06.2007 11:42:26]

#

Riittääkö se, että annat komennon chcp 28591 ennen ohjelmasi ajoa (tai ohjelma sisältäkin voi toimia)? Tämä vaihtaa merkistön koodisivua. En ole edes varma, mitä tuo kyseinen numero tarkoittaa, mutta onnistuin löytämään sen erään ohjelman avulla, ja se korjasi omat merkistövaivani. Voit ainakin kokeilla.

Merkistön muutokseen pitäisi olla valmis funktio jossain Windows API:n uumenissa. Omankin tekeminen on suoraviivaista, jos tiedät mitä merkkejä teksteistäsi voi ylipäätänsä löytyä. Siltä varalta, että käyttäjä on itse vaihtanut merkistöään, voi tämän muunnoksen tehdä valinnaiseksi jonkinlaisen konfiguraatiojutskan avulla.

Deewiant [24.06.2007 12:24:19]

#

Jos jokin muu kirjasto ei jo hoida hommaa puolestasi, kannattaa varmaan tehdä jonkinlainen oma print-funktio, joka sitten Windowsin puolella kutsuu WriteConsoleW()-funktiota, Linuxilla homma onnistunee helpoiten käyttämällä locale.h:n funktioita ja printf:llä %ls-formaattia.

Et maininnut kieltä, joten teen esimerkin C:llä, vaikka olenkin vähän ruosteessa. Windows-koodi näyttäisi vaikka tältä:

int print(char* src) {
	int srcLength = strlen(src);

	if (!srcLength)
		return 0;

	DWORD bufferLength = MultiByteToWideChar(CODEPAGE, 0, src, srcLength, 0, 0);

	if (!bufferLength)
		return 0;

	/* en nyt jaksa katsoa miten mallocia kutsutaan */
	WCHAR* buffer = malloc(sizeof(WCHAR) * bufferLength);

	if (!buffer)
		return 0;

	if (!MultiByteToWideChar(CODEPAGE, 0, src, srcLength, buffer, bufferLength))
		return 0;

	/* WriteConsole syö max. 64 KB kerrallaan, eli 32 KB UTF-16:a */
	/* en nyt viitsi ottaa sitä huomioon */

	DWORD written;

	if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), buffer, bufferLength, &written, 0))
		return 0;

	return written;
}

CODEPAGE:n arvon saat keksiä itse, etsi vaikka MSDN:n Code Page Identifiers -sivulta. Lienee 1252 taikka 28591.

Linuxilla taas homma toimii käsittääkseni suunnilleen samalla tavalla, käytät vain mbstowcs-funktiota ja printf:ää. En ole ihan varma miten sillä puolella locale-hommat tarkalleen ottaen toimii, joku asiantuntevampi pystynee kertomaan tarkemmin.

FrozenFire [24.06.2007 12:51:13]

#

Linuxilla kaikki toimii utf-8 pohjalla ja mitään ongelmaa ole. Muuntaako tuo pdcurses jo merkistöä kun "normaali" ä-kirjaimen koodi \x84 onkin \xe4.

Windows käyttää ilmeisesti latin-1:stä johon tuo \xe4 viittaisi. Nyt vaan mietin että kumpi ois helpompaa, laittaa ohjelma muuntamaan utf-8 winowsin ymmärtämään muotoon. Vai vaihtaa ohjelman merkistö windowsille helpoksi ja muuntaa merkit sitten utf-8:si?

Ja jos pdcurses pyörittää merkistöä jo oletuksena niin onko sitä mahdollista säätää sieltä käsin?

En ole aikoihin windowsin puolella ohjelmoinut ja nyttenkin käännän ohjelmat suoraan linuxista käsin. WinAPI-systeemeihin en ole ikinä perehtynyt. Ohjelma on tosiaan C++-kielinen, mutta johan siihen tarpeen tullen liittää c-kieltäkin.

Deewiant [24.06.2007 13:18:20]

#

FrozenFire kirjoitti:

Linuxilla kaikki toimii utf-8 pohjalla ja mitään ongelmaa ole.

lainaus:

Windows käyttää ilmeisesti latin-1:stä johon tuo \xe4 viittaisi.

Kummallakin käyttöjärjestelmällä homma riippuu käyttäjän asetuksista. Windowsin konsoli käyttää kuitenkin aina oletuksena UTF-16:a.

lainaus:

Nyt vaan mietin että kumpi ois helpompaa, laittaa ohjelma muuntamaan utf-8 winowsin ymmärtämään muotoon. Vai vaihtaa ohjelman merkistö windowsille helpoksi ja muuntaa merkit sitten utf-8:si?

Windowsin konsoli syö UTF-16:a, en muista kumpaa endiaanisuutta. Helpointa on käyttää sisäisesti aina UTF-8:a, sitten konvertoida MultiByteToWideChar(CP_UTF8, ...)-kutsulla UTF-16:ksi cmd.exea varten.

lainaus:

Ja jos pdcurses pyörittää merkistöä jo oletuksena niin onko sitä mahdollista säätää sieltä käsin?

Uusimmissa PDCurseseissa on jotain Unicode-tukea. En ole tutkinut asiaa tarkemmin, kun en ole sitä viime aikoina käyttänyt, mutta lueskele sen dokumentaatiosta. ncursesista taas en tiedä: muista, että PDCursesilla ja ncursesilla on eronsa. Testaile molemmilla alustoilla ahkerasti, niin näet miten homma toimii.

GoldenDragon [25.06.2007 02:47:15]

#

Windowsissa kätevä pikkukikka ääkkösten näkyviin saamiseen on avata lähdekoodi edit-komentoriviohjelmalla ja tallentaa se. Aika purkka, mutta pienissä ohjelmissa ihan kätevä. ;)

FrozenFire [25.06.2007 09:00:38]

#

Mutta tuo ei taida toimia pdcursesin kanssa joka syö tuntuu windowsin merkistöä komentorivin merkistön sijaan. Ja edelleen kääntö tapahtuu täysin linuxista käsin.

Metabolix [25.06.2007 12:53:35]

#

Parasta lienee tehdä jokin tuollainen tekstinmuuntofunktio, tällöin kannattaa lukea MSDN:stä kuvaukset funktioille MultiByteToWideChar ja WideCharToMultiByte. Lisäksi Windowseissa on ainakin tähän asti ollut sellainen ominaisuus, että WideChar on ollut käytännössä UTF-16.

Teinpä tarkoitukseen funktion, joka ainakin Winellä Windows-versio tuotti toivotun tuloksen ja natiivi Linux-käännöskin toimi. Testiohjelmassa tulostin tällä UTF-8-tekstitiedoston. Ohjelman alussa oli määritelty makrolle WINDOWS jokin arvo, käännöksessä annoin tarvittavan lipun -DTARGET=WINDOWS tai -DDEFAULT_UTF8=1.

#include <string>
#include <vector>

#if DEFAULT_UTF8

std::string utf8_to_str(const char *src)
{
	return std::string(src);
}

#elif (TARGET == WINDOWS)

#include <windows.h>
std::string utf8_to_str(const char *src)
{
	int error, size;
	std::vector<WCHAR> buf;
	std::string ret;

	size = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0);
	buf.resize(size);
	error = MultiByteToWideChar(CP_UTF8, 0, src, -1, &*buf.begin(), buf.size());
	// Ja tarkistus virheen varalta (tee-se-itse)

	size = WideCharToMultiByte(CP_ACP, 0, &*buf.begin(), buf.size(), 0, 0, 0, 0);
	ret.resize(size);
	error = WideCharToMultiByte(CP_ACP, 0, &*buf.begin(), buf.size(), &*ret.begin(), ret.size(), 0, 0);
	return ret;
}
#else
#error TARGET != WINDOWS && !DEFAULT_UTF8
#endif

Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta