UTF-8 codierte Datei einlesen

Crai

Mitglied
Hallo,

meine Aufgabe besteht darin, eine UTF-8 codierte Datei einzulesen und bestimmte Vorkommen eines Zeichens darin zu markieren, d.h. die Position der Markierung wird gespeichert.

Um das zu erreichen, lese ich den Inhalt der Datei byteweise in einen String ein und such in diesem String nach dem betsimmten Zeichen.
Nun das Problem:
Das byteweise Einlesen funktioniert bei UTF-8 nur im Bereich der ASCII-Zeichen, da diese 1 zu 1 auf UTF abgebildet werden. Bei anderen Zeichen geht das nicht mehr, da diese in UTF-8 durch 2 oder 3 Bytes codiert werden. Das erkennt aber mein kleiner Algorithmus nicht und ich ermittle in Folge dessen falsche Positionen.

Nun meine Frage: Gibt es eine Möglichkeit, eine UTF-8 codierte Datei in einem Rutsch richtig konvertiert in einen String einzulesen, den ich dann weiterverarbeiten kann

Ich habe in diesem Zusammenhang schon die von DataInput bereitgestellte Methode readUTF() ausprobiert, doch diese funktioniert nur, wenn die Daten vorher mit der Methode writeUTF() in die Datei auch geschrieben werden. ;(

Vielen Dank für jeden Tipp!

Grüße,
Crai
 
Vielen Dank für deinen Tipp!! ;-)

Ein Problem besteht dabei aber noch: Wie weiß ich, welches/bzw. wie viele
Zeilenumbruchzeichen zum Zeilentrennen in der Datei verwendet werden?
Ich muss ja die Anzahl der Zeilenumbruchzeichen auch mit einrechnen, wenn ich
die richtige Position bestimmen will

Gruß,
Crai
 
Hallo Crai,

der Zeilenumbruch wird von den Betriebssystemen unterschiedlich gehandhabt. Das heißt es gibt verschiedene Zeichen die für einen Zeilenumbruch stehen.

Diese wären:

\n
\r
\r\n

Du kannst mit

String zeilenumbruch = System.getProperty("line.seperator");

das Zeichen für den Zeilenumbruch des Betriebssystems erfragen um plattformunabhängig zu arbeiten.

Ließ dir noch kurz folgendes durch:
http://www.galileocomputing.de/open...el_08_003.htm#Rxx365java08003040002891F04C10F

Noch eines vielleicht:

Um die Position eines bestimmten Zeichens in einem String zu finden hast du eine Menge Methoden, bsp.: indexOf. Der Achtet schon für dich auf die Zeilenumbrüche und gibt die richtigen Positionen.

String s = "a\nb";
s.indexOf("a"); // ergibt 0
s.indexOf("b"); // ergibt 2


Vg Erdal
 
Danke Erdal für deine Hilfe!! :)

Vielleicht hilft es, wenn ich mein Problem etwas genauer und allgemeiner beschreibe:
Ich lese eine UTF-8 codierte Datei, zum Beispiel mit deinem Codeschnipsel aus dem Java-Almanach, in einen String ein.
Die Methode "read()" wird dabei benutzt. Diese gibt einen Wert zwischen 0 und 65535 (2 Bytes) zurück.
The character read, as an integer in the range 0 to 65535 (0x00-0xffff), or -1 if the end of the stream has been reached
siehe: BufferedReader#read()

So, wenn ich nun einen zurückgelieferten Integer-Wert bekomme, der zwischen 0 und 255 liegt, ist es für mich kein Problem, diesen in den entsprechenden Character umzuwandeln.
Nur was mache ich, wenn ich einen höheren Integer-Wert zurückgeliefert bekomme?
So etwa der folgende Ausdruck nicht, wenn c=65279 ist: String.valueOf((char)c)
In solchen Fällen wird immer ein Fragezeichen zurückgeliefert.

Ich dachte aber fälschlicherweise, dass das funktionieren müsste, da nachdem was ich gelesen habe, Java intern mit UTF-16 arbeitet!? :confused:

Hier noch meine (vereinfachte) nicht zufrieden stellende Lösung. Alle Zeichen mit einem Wert > 255 werden nicht verarbeitet, da diese nicht in den richtigen Char umgewandelt werden können.
Code:
			//***************** Read file content and write it to a string ***********************
			System.out.println("Charset of file (" + file.getName() + "): " + file.getCharset());
			BufferedReader input = new BufferedReader(new InputStreamReader(file.getContents(), file.getCharset()));
	        String strContent = "";
	        int c;
	        while ((c=input.read())!=-1)
	        {
	        	if (c < 256)
	        	{
	        		strContent += String.valueOf((char)c).toUpperCase();
	        	}
	        	else
	        	{
	        		System.err.println("Found invalid char value in file(" + file.getName() + "): value=" + c);
	        	}
	        }
	        //now the variable strContent contains the content in upper case

Gruß,
Crai
 
Hallo!

Haeng doch bitte einfach mal eine Datei mir der entsprechenden Codierung an und beschreibe die gewuenschte Ausgabe.

Gruss Tom
 
Hallo Crai,

eine Hilfsklasse (UnicodeReader) die ich aus dem Web vor kurzer Zeit gefunden habe und eine weitere von mir erstellte Klasse (ReadUTF8Example) die diesen benutzt.

Ich habe das encoding hier in dem Beispiel nicht explizit angegeben, da der UnicodeReader das in der Regel selbst erkennt und richtig einliest.

Dann habe ich zum einlesen, die read() Methode einer JTextArea verwendet, da dieser eine Datei vollständig einliest und in einen String übergibt. Es könnte auch jede andere Textkomponente genommen werden.

Der UnicodeReader hat mir persönlich so gut gefallen, dass ich den in meiner Notepad-Applikation eingebaut habe.


Vg Erdal

Code:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.swing.JTextArea;

public class ReadUTF8Example {
	
	public static void main(String[] args) {
		
		JTextArea textread = new JTextArea();
		
		String text = "";
		String path = "test.txt";
		String encoding = null;
		
		try {
			textread.read(new UnicodeReader(new FileInputStream(
					path),encoding), "");
		} catch (FileNotFoundException e) {
			System.out.println(e.getMessage());
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
		text = textread.getText();

		System.out.println(text);
	}
}

Code:
/**
 Original pseudocode   : Thomas Weidenfeller
 Implementation tweaked: Aki Nieminen

 http://www.unicode.org/unicode/faq/utf_bom.html
 BOMs:
 00 00 FE FF    = UTF-32, big-endian
 FF FE 00 00    = UTF-32, little-endian
 FE FF          = UTF-16, big-endian
 FF FE          = UTF-16, little-endian
 EF BB BF       = UTF-8

 Win2k Notepad:
 Unicode format = UTF-16LE
 ***/

import java.io.*;

/**
 * Generic unicode textreader, which will use BOM mark
 * to identify the encoding to be used. If BOM is not found
 * then use a given default encoding.
 * System default is used if:
 *    BOM mark is not found and defaultEnc is NULL
 *
 * Usage pattern:
 String defaultEnc = "ISO-8859-1"; // or NULL to use system default
 FileInputStream fis = new FileInputStream(file);
 Reader in = new UnicodeReader(fis, defaultEnc);
 */
public class UnicodeReader extends Reader {
	PushbackInputStream internalIn;

	InputStreamReader internalIn2 = null;

	String defaultEnc;

	private static final int BOM_SIZE = 4;

	UnicodeReader(InputStream in, String defaultEnc) {
		internalIn = new PushbackInputStream(in, BOM_SIZE);
		this.defaultEnc = defaultEnc;
	}

	public String getDefaultEncoding() {
		return defaultEnc;
	}

	public String getEncoding() {
		if (internalIn2 == null)
			return null;
		return internalIn2.getEncoding();
	}

	/**
	 * Read-ahead four bytes and check for BOM marks. Extra bytes are
	 * unread back to the stream, only BOM bytes are skipped.
	 */
	protected void init() throws IOException {
		if (internalIn2 != null)
			return;

		String encoding;
		byte bom[] = new byte[BOM_SIZE];
		int n, unread;
		n = internalIn.read(bom, 0, bom.length);

		if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB)
				&& (bom[2] == (byte) 0xBF)) {
			encoding = "UTF-8";
			unread = n - 3;
		} else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
			encoding = "UTF-16BE";
			unread = n - 2;
		} else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
			encoding = "UTF-16LE";
			unread = n - 2;
		} else if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00)
				&& (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
			encoding = "UTF-32BE";
			unread = n - 4;
		} else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)
				&& (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
			encoding = "UTF-32LE";
			unread = n - 4;
		} else {
			// Unicode BOM mark not found, unread all bytes
			encoding = defaultEnc;
			unread = n;
		}
		//      System.out.println("read=" + n + ", unread=" + unread);

		if (unread > 0)
			internalIn.unread(bom, (n - unread), unread);

		// Use given encoding
		if (encoding == null) {
			internalIn2 = new InputStreamReader(internalIn);
		} else {
			internalIn2 = new InputStreamReader(internalIn, encoding);
		}
	}

	public void close() throws IOException {
		init();
		internalIn2.close();
	}

	public int read(char[] cbuf, int off, int len) throws IOException {
		init();
		return internalIn2.read(cbuf, off, len);
	}
}
 
Hallo,

sorry, war ein paar Tage ohne www!
Vielen dank Erdal, mit deinem Code konntest du mir weiterhelfen! :)

@tom:
Ich glaube das hat sich erledigt. Hab bemerkt, dass ich einen kleinen Knoten im Hirn hatte, deswegen hab ich mich teils auch mal ein bißchen konfus ausgedrückt.
Auch dir vielen Dank- auch für die vielen andren Male, wo du mir geholfen hast! :)

Schönen Sonntag noch!

Grüße,
Crai
 
Zurück