JTextField kodowanie

0

Mam taki problem. W tablicy byte trzymam różne reprezentacje znaków, niektóre są bardzo dziwaczne i jak wrzucam to w Stringa to sobie nie radzi z wyrzuceniem tego na kontrolkę JTextField. Wydaje mi sie ze jakoś kodowanie musze zmienić jednak nie wiem jak to zrealizować albo mozna jakoś inaczej uporac się z tym błędem?

0

Najpierw upewnij się, że System.out.print wyrzuca na konsolę dokładnie to co chcesz. A potem:

JTextField textField = new JTextField();
textField.setText(new String(bytesArray, "UTF-8"));

przy założeniu, że to co masz w tablicy jest w UTF-8.

Dla samego JTextField nie ustawia się kodowania.

0

Nie działa.
Standardowe dla windowsa to cp1250 i jakie w Eclipse mam ustawione i niektóre znaczki na konsoli są znakami zapytania, których nie może string wypluć. Dając UTF-8 jest jeszcze gorzej.
Kurde to już nie wiem jak zrobić aby wszystko dało się wyświetlić.

0

Takie wartości mam w tablicy
-15 -81 127 66 -16 32 88 -39 25 -64 -30 -1 -71 -41 -25 99
wyświetlić nie może się 127 i 25 reszta jakieś wygibasy wywala.

0

Tylko Ty wiesz jakie masz kodowanie, takiego musisz użyć.

0

W Eclipsie mam cp1250 jednak dając do Stringa też takie nie ma róznicy. Nadal te dwa znaki się wywalają. A w innych kodowaniach to juz same ???

0

Jak chcesz wyświetlać dziwne znaki, to korzystaj z Unicode.

0

To dość absurdalna sytuacja, gdy programista ma tablicę, ale nie wie jakiego kodowania używa. Trudno będzie pomóc.

0

Bierzecie pod uwage, ze te znaki zaleza rowniez od czcionki? Np. powiedzmy Times New Roman moze nie pokazac znakow arabksich (akurat nie wiem czy tak jest, to tylko przyklad) mimo ze sa poprawnie zdekodowane - czcionka nie ma po prostu odpowiednich glyphow.

1

Autor zapewne chce wyświetlać nietypowe znaki (np. strzałki). Wtedy trzeba wykorzystać Unicode i używać czcionki Dialog, ona "zna" najwięcej znaków. Przykład

int[] kody = {0x2190,0x2191,0x2192,0x2193,0x2194,0x2195};
String txt = new String(kody,0,kody.length);
0

Wszystkie teksty w stringach buforach znakowych w Javie (w pamięci) są trzymane/kodowane w UTF-16 BE (big endian - najpierw górny bajt, potem dolny). Cokolwiek jest napisane w kodzie źródłowym Javy jest przekształcane w bytecode na kodowanie UTF-16BE. Dzięki temu wszystkie znaki jakiekolwiek znajdą się w kodzie (za wyjątkiem chińskich potrzebujących kodowania 32-bitowego) mogą być indeksowane co jedno słowo 16-bitowe. Tak są kodowane wewnętrzne wszelkie obiekty String, StringBuffer, StringBuilder, CharBuffer, JtextField itd. Jeżeli masz tablicę bajtów i próbujesz ją traktować jako tekst, to Java będzie to zawsze interpretować jako tekst w UTF-16BE. Dopóki używane będzie tylko pierwsze 7 bitów odpowiadających kodowaniu ASCII, to nic się nie będzie sypać za wyjątkiem tego, że właściwe kody będzie zawierać do co drugi bajt. Tak to jednak działa tylko u anglosasów. Dzięki temu mogą pisać śmieciowy soft źle obsługujący znaki i nie zobaczą u siebie żadnego problemu.

Jednak jeżeli chce się to obsługiwać poprawnie, to trzeba korzystać z kodowania tekstów z Unicode do dowolnego innego zestawu lub dekodowania z innego zestawu do Unicode. Robią to jednorazowo metody Charset.encode() i Charset.decode().
Najpierw należy uzyskać obiekt Charset określający kodowanie inne niż Javowe UTF-16BE. Jeżeli ma to być domyślne dla bieżącej lokalizacji systemu kodowanie 8-bitowe, to najlepiej użyć Charset.defaultCharset(), w innym wypadku trzeba albo użyć Charset.availableCharsets() i wybrać właściwy, albo na sztywno Charset.forName() z kanoniczną nazwą kodowania (uwaga na dwie różne nazwy obowiązujące w nowszych i starszych pakietach Javy).
Opis wszystkich obsługiwanych kodowań jest tu: http://docs.oracle.com/javase/1.5.0/docs/guide/intl/encoding.doc.html (zapewne trochę niepełny w Javie 7)

Żeby odczytać plik lub jakiś bufor z kodowaniem innym niż domyślny unikod musisz użyć metody Charset.decode(ByteBuffer bb). Jednak żeby zrobić to samo z tablicą trzeba ją opakować przez ButeBuffer.wrap(byte[] array). W efekcie dekodowania otrzymasz CharBuffer z poprawnie zdekodowanymi znakami, które nie będą żadnymi znaczkami. O ile dobrze określiłeś kodowanie CharSet dla konkretnego ciągu bajtów (np. byte[]).

Jeżeli masz dużo tablic do dekodowania z tym samym kodowaniem, to efektywniej jest utworzyć jednorazowo Charset.newDecoder() i użyć jego metody decode wielokrotnie na różnych buforach/tablicach otrzymując CharBuffer, który implementuje CharSequence, a także posiada metodę toString(). Taki bufor znakowy ma już zawsze poprawne znaki w obowiązującym w pamięci JVM kodowaniu UTF-16BE. Oczywiście o ile decode() nie rzuciła wyjątkiem lub CodeResult.isError() jej drugiej wersji nie dał true. W przypadku kodowania do UTF-16BE lub dekodowania z niego operacja jest trywialna bo nic nie robi, w przypadku kodowania lub dekodowania z UTF-16LE zamieniane są miejscami kolejne pary bajtów. W każdym innym wypadu decode i encode muszą alokować pamięć na nowe dane.

Jeżeli ktoś chce sobie wrzucić dane do pliku w dowolnym kodowaniu i z powrotem, to można to zrobić np. tak:

package com.olamagato.test;
import java.util.SortedMap;
import java.nio.file.Path;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import javax.swing.filechooser.FileSystemView;
import static java.nio.file.StandardOpenOption.*;


public class TextFilesJDK7
{
	public static void main(String[] args)
	{
		Path dir = FileSystemView.getFileSystemView()
			.getDefaultDirectory().toPath();
		String[] csnames =
		{
			"UTF-16LE", "UTF-8", "windows-1250", "IBM852", "ISO-8859-2"
		};
		SortedMap<String, Charset> available = Charset.availableCharsets();
		for(String csn: csnames)
			if(available.containsKey(csn))
			{
				Charset cs = available.get(csn);
				StringBuilder name = new StringBuilder(cs.name());
				if(cs.equals(Charset.defaultCharset()))
					name.append(" (domyślny)");
				Path fn = dir.resolve("Plik typu " + name + ".txt");
				saveVerify(fn, cs);
			}
	}

	private static void saveVerify(final Path fn, final Charset cs)
	{
		//zapis
		try { Files.deleteIfExists(fn); } catch(IOException ex) { return; }
		try (FileChannel file = FileChannel.open(fn, CREATE, WRITE))
		{
			file.write(cs.encode(CharBuffer.wrap(
				"Jakiś polski tekst ąćęłńóśźż w kodowaniu "
				+ cs.name() + "\r\n")));
		}
		catch(IOException ex) { return; }

		//odczyt (weryfikacja)
		CharSequence wynik = null;
		try(FileChannel file = FileChannel.open(fn))
		{
			wynik = cs.decode(read(file, newBuffer(file)));
			//porównanie bez dekodowania:
			//wynik = read(file, newBuffer(file)).asCharBuffer();
		}
		catch(IOException ex) { return; }
		System.out.printf("Plik: %s\nWynik:\n%s\n", fn.normalize(), wynik);
	}

	private static ByteBuffer newBuffer(FileChannel file) throws IOException
		{ return ByteBuffer.allocateDirect((int)file.size()); }

	private static ByteBuffer read(FileChannel file, ByteBuffer bb)
		throws IOException { file.read(bb); bb.flip(); return bb; }
}
/*
Output:
Plik: E:\Dane\Olamagato\Plik typu UTF-16LE.txt
Wynik:
Jakiś polski tekst ąćęłńóśźż w kodowaniu UTF-16LE

Plik: E:\Dane\Olamagato\Plik typu UTF-8.txt
Wynik:
Jakiś polski tekst ąćęłńóśźż w kodowaniu UTF-8

Plik: E:\Dane\Olamagato\Plik typu windows-1250 (domyślny).txt
Wynik:
Jakiś polski tekst ąćęłńóśźż w kodowaniu windows-1250

Plik: E:\Dane\Olamagato\Plik typu IBM852.txt
Wynik:
Jakiś polski tekst ąćęłńóśźż w kodowaniu IBM852

Plik: E:\Dane\Olamagato\Plik typu ISO-8859-2.txt
Wynik:
Jakiś polski tekst ąćęłńóśźż w kodowaniu ISO-8859-2
*/

Programik zapisuje pliki na dowolnym systemie, ale zapisuje końce wierszy zawsze na modłę z Windy (CRLF). Ostatni plik nie jest możliwy do poprawnego odczytania standardowym notatnikiem Windows XP, ale bez problemu z właściwymi znakami otwiera go każda przeglądarka

Osobną sprawą jest reprezentacja wszystkich kodowanych znaków w plikach czcionek. W teorii powinna istnieć przynajmniej jedna czcionka, która wyświetli wszystkie poprawne znaki unikodu. Problem polega na tym, że np. w Windows większość plików czcionek truetype jest kodowana 8-bitowo i ograniczona do swojego krótkiego zestawu znaków. Na przykład na Windows XP istnieje tylko jedna ogromna czcionka "Arial Unicode MS" (arialuni.ttf, 23MB), która obsługuje większość kodów z całej tablicy UTF-16. Wszystkie inne są w razie renderowania mapowane z powrotem do zestawu znaków, który czcionka obsługuje (dlatego wszystkie inne kody znaków świecą dziurami mimo poprawnego kodowania). Zresztą inne systemy też nie są pod tym względem lepsze.

ps. Kodowanie w jakim jest plik źródłowy Javy (Windows-1250, nie ma znaczenia ponieważ nawet jeżeli jest to Cp1250, UTF-8 czy IBM852, to taki Eclipse uwzględnia to i przekazuje kompilatorowi informację o tym. Kompilator to uwzględnia i przekształca na postać UTF-16BE, którą umieszcza w bytecode.
Kiedyś kod źródłowy Javy też musiał być w unikodzie, żeby poprawnie kodował znaki narodowe użyte w stringach.

0

Brawo Olo, piekny wywod. Jak sie to jednak ma do opisanego przez OP problemu to nie wiem, nigdzie nie bylo mowy o plikach o ile sie nie myle. Aha, znaki chinskie rowniez mozna w kodzie zapisac, jednak nie jako literały, a jako pary surogatow (tj. sam sobie musisz zakodowac je w utf16 czy co tam Java uzywa).

0

To zakoduj mi literał znakowy z intami, bez par surogatow, dla takich 32 bajtowych znakow.

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