PowerShell - UTF8 to CP852 (változó értékének konvertálása más kódlapra)

Fórumok

Sziasztok,

van egy LDAP lekérdezésem PowerShellben, ami egy UTF8 stringet ad eredményül. Az értéket be kellene állítanom környezeti változóként (SetEnvironmentVariable), viszont ott nem jó az UTF8 érték, preferáltan CP852 kellene, de a CP1250 sem rossz megoldás, ha cmd.exe-ből dolgozom fel az eredményt.

Teljesen süket vagyok a PowerShellhez a Google pedig nem a barátom: főként olyan találatok jönnek, hogyan lehet valamit UTF8-ra alakítani, illetve, a mindenféle fájlba író/olvasó funkciók "-Encoding" opcióját hozza, ami nekem most nem releváns.

PowerShell guruk, kérlek segítsetek! Köszönöm! :)

Hozzászólások

Milyen LDAP szerver? Active Directory vagy valami más?

Alapvetően a PowerShell (és a Windows magában is, beleértve az Active Directory-t) a sztringeket karakterenként két byte-on, UCS-2 karakterkódolással kezeli (vagy mondhatjuk UTF-16-nak is, bár nem teljesen ugyanazt jelenti). Ez igaz az environment változókra is, a tárolásuk a memóriában karakterenként két byte. Az egybyte-os kódlapok Windows alatt inkább csak egyfajta kompatibilitási réteget jelentenek, és erősen szuboptimális a használatuk bármire is. Az UTF-8 pedig a PowerShell világában egy külsőbb tárolási forma, ha file-ba vagy hálózatra írsz sztringből eredő adatot, akkor értelmezhető, hogy UTF-8-ban lesz, de a PowerShell memóriában mint sztring változó biztosan nem.

Ezért kérdéses, hogy mit is értesz az alatt, hogy "PowerShellben, ami egy UTF8 stringet ad eredményül". Ha PowerShell-ben már van egy sztring változó, és ott helyesen vannak az ékezetek, tehát pl. egy Write-Host hívás jól jeleníti meg a változó értékét, akkor ott már nincs semmi UTF-8, ott UCS-2 van. Ha viszont a Write-Host már "furcsa" karakterekkel jeleníti meg az ékezetek értékét, akkor már eleve a PowerShell-be rosszul került bele, onnan ne is dolgozz vele tovább.

Mit jelent azt, hogy cmd.exe -ből dolgozod fel az eredményt? Ott kezd problémássá válni a dolog, ha valami olyan .exe file-nak akarod mondjuk paraméterként átadni, ami nem UCS-2 alapú, hanem egybyte-os kódlap alapú. Pl. ha C nyelven írt programnak adod át, ami nem wchar_t, hanem char típussal fogadja a parancssori paramétereket, az az ilyen esetekre erősen szuboptimális. Ott könnyen elvesznek az ékezetes karakterek.

Nem Active Directory, de jelen esetben ez látszólag nem releváns, van a PowerShell-ben egy változóm, ami ékezethelyes stringet tartalmaz. (Mellékes körülmény, hogy a létrejötte egy LDAP lekérdezés eredménye volt, az LDAP címtárban pedig eredendően egy UTF8 kódolású string van letárolva, de nekem nem fáj, ha a PowerShell "házon belül" már UCS-2-re alakította.)

A feldolgozás egy DOS batch fájlból történik, ami mindenféle DOS-os programot hívogat. (kódlap alapút) Mivel - gondolhatod - egy meglévő, 30 éve működő megoldást szeretnék kiváltani, ezért a legjobb megoldás az, ha ugyanazzal a kódolással állítom be a környezeti változót, mint amiben korábban volt.

A PowerShell, és az abból hívott batch file, amikor még a cmd.exe futtatja, szintén UCS-2 világban van. A DOS-os .exe programba való áthívásnál történik az izgalom. Ilyenkor ha jól tudom, az ANSI kódlapra (ami nem egy konkrét dolog, hanem mindig az adott rendszertől függ, pl. magyar beállításű windows-on az 1250) próbál konvertálni UCS-2 -ről. Ha itt vannak olyan karakterek, amik nem ábrázolhatók a cél 1 byte-os kódlapon, akkor ott lesznek a problémák.

Amit meg kéne nézned: mi az ANSI kódlap az adott rendszeren, ill. a DOS-os program milyen karakterkódolást vár. Ill. annak is utána lehet nézni, hogy ez konverzió akár nem rendszerszinten az ANSI kódlappal, hanem akár session szinten is szabályozható-e, ezt most nem tudom fejből.

Szerkesztve: 2025. 01. 31., p – 16:23

Így tudsz a Windows parancssorában, illetve a Powershell parancssorban (ha nem URF-8 a kódolás - ezt a chcp paranccsal tudod lekérdezni) kiíratni:

[System.Console]::OutputEncoding = System.Text.Encoding]::GetEncoding(1250)
$text = "idejön az UTF-8 kódolású szöveg, ami lehet egy program kimenetew is"
Write-Output $text

Szerintem erre gondoltál.

Elrontottam a zárójelezést, bocs. A kiegészített kód:

[System.Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(1250)
$text = "idejön az UTF-8 kódolású szöveg, ami lehet egy program kimenete is"
[Environment]::SetEnvironmentVariable("rendszerváltozó", $asciiString, "User") #így írod be változóba
Write-Output $text #ez meg csak a példa miatt volt

Elvileg jó,

Ja ez ilyen, zseniális húzás volt a Májrkémszafttól ez az UCS2 keverés egy UTF8-as világban, azóta is megszépíti sok ember napját. Mert ők nem tudnak ám UTF8-azni, ahogy mindenki más, ki kellett találni sajátot, újra feltalálni a kereket.

Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”

Nyilván ezt is jobban tudod, hogy mi miért és mikor történt. Anélkül, hogy esetleg utánanéznél a valóságnak és a tényeknek. Segítek: a Microsoft elég korán (korábban, mint mások) eldöntötte, hogy normálisan támogatni fogja az ékezeteket, a Windows NT 3.1 már úgy érkezett 1993-ban, hogy UCS-2 -t használ, ami az akkori Unicode szabvány szerint az összes lehetséges karaktert árbázolni tudja, anélkül hogy kódtáblák közt kéne átalakítgatni. Ebből ered, hogy Windows alatt a sztringek karakterenként 2 byte-osak. És ez még bőven az előtt volt, hogy "UTF8-as világ" (idézet tőled) lett volna, hiszen akkor még nem volt UTF-8 világ és szabvány, amikor ők már fejlesztettek és aztán ki is hoztak egy olyan terméket, ami az összes akkori karaktert ábrázolni tudta. Némi olvasnivaló: https://devblogs.microsoft.com/oldnewthing/20190830-00/?p=102823

És ha esetleg közelebbről, mondjuk fejlesztői oldalról is hozzászagoltál volna ehhez, tudnád, hogy minimális odafigyelés mellett el lehet érni, hogy jól működjenek UCS-2 mellett a C/C++ nyelven írt programok, ne vesszenek el  még akkor sem sehol az ékezetek, amikor mondjuk parancssori paraméterátadás, környezeti változó átadás stb. történik, hiszen ez a fajta karakterábrázolás a kernelen belül is így van implementálva. Olvasnivaló ehhez: https://stackoverflow.com/questions/13509733/what-is-the-use-of-wchar-t… A topiknyitó kérdésben gondolom olyan programok vannak használva parancssorból, amik nem felelnek meg ennek az elvnek.

Kieg: szintén a 16-bites (UCS2, megpatkolva UTF-16) vonatra szállt fel az Oracle, a Java, az Aix. A többiek később ébredtek, és UTF8-ra leltek.

Szerk: a wchar_t meg még az unicode előttről való, és a pontos definíciója az, hogy "platformfüggő valami, aminek az értékkészlete legalább akkora, mint a `char`-nak'