Input/Output FileReader UTF-8 Datei

  • Themenstarter Themenstarter Stephan222
  • Beginndatum Beginndatum
S

Stephan222

Gast
Hallo,

ich habe folgendes Problem:

Ich lese Zeile für Zeile aus einer Textdatei per BufferedReader.
Nun ist mir aufgefallen, dass falls die Datei UTF-8 kodiert ist, in der ersten Zeile immer ein zusätzliches "leeres" Zeichen enthalten ist.
Dieses würde ich gerne aus meinem String bekommen, weiß aber nicht wie ich das anstellen soll bzw. wie ich abfragen kann ob dieses Zeichen vorhanden ist.
Es ist ja auch nur bei UTF-8 Dateien und immer nur in der ersten Zeile und nicht generell.

So sieht der Code aus:
Java:
ArrayList<String> result = new ArrayList<String>();
BufferedReader in;
try {
	in = new BufferedReader(new FileReader(file.getAbsoluteFile()));
	
	String zeile = null;
	
	while ((zeile = in.readLine()) != null) {
		result.add(zeile);
	}
			
	in.close();
} catch (IOException e) {
	log.error("Read input file failed: " + e.getMessage());
}

Im Debugger sieht es so aus:
empty01w5.png


Wichtig ist, dass es kein Leerzeichen ist sowie der Value mit dem Index 18 auf dem Bild.
Wenn ich in das Feld reinklicke, ist es einfach nur leer.

Aber sowas wie: zeile.charAt(0) == '' geht ja nicht abzufragen >.>

Wie würdet ihr vorgehen?
 
Der BOM dürfte es nicht sein, der ist mindestens 2 Bytes/Zeichen lang.
Probiere doch mal String#trim(). Ansonsten schau mal nach welchen Code das Zeichen hat. Dazu am einfachsten einen Hexeditor nehmen. Dann kannst du zumindest mit getCharAt(0) == 4711 vergleichen ob es das Zeichen ist.
 
Also ich habe die Datei mal mit einem Hex Editor geöffnet und am Anfang steht sowas:
EF BB BF
Also als ASCII Zeichen: 
 
Nachtrag:
Scheint wohl doch dieses BOM zu sein, weil ich wenn ich mite Notepad++ die Datei öffne und bei Kodierung dann "Konvertiere zu UTF-8 ohne BOM" auswähle, abspeichere und das dann noch mal debugge, dann ist das Zeichen vorweg verschwunden.
Nun ist die Frage: Wie bekomme ich bei Dateien mit BOM, dieses BOM weg?
 
Ja, das ist der BOM. Den bekommst du weg indem du ihn überliest. Evtl hilft es auchschon nicht FileReader sonder InputStreamReader zu verwenden und das passende Encoding mit anzugeben. Denn so wie du das momentan machst liest du als iso-8859-1 ein.
 
Das ist schon seit Java 1.4.2 als "Bug" gemeldet, wird aber aus "Kompatibilitätsgründen" nicht geändert -.-

Bug ID: 4508058 UTF-8 encoding does not recognize initial BOM

Dir bleibt wohl nix anderes als den BOM am Anfang selbst zu überspringen.

Eventuell hilft dir das (ungetestet):
Java:
package bom;

enum BOM
{
	
	BOM_UTF_8(0xEF, 0xBB, 0xBF),
	BOM_UTF_16_BE(0xFE, 0xFF),
	BOM_UTF_16_LE(0xFF, 0xFE),
	BOM_UTF_32_BE(0x00, 0x00, 0xFE, 0xFF),
	BOM_UTF_32_LE(0xFF, 0xFE, 0x00, 0x00),
	BOM_UTF_7(0x2B, 0x2F, 0x76),
	BOM_UTF_1(0xF7, 0x64, 0x4C),
	BOM_UTF_EBCDIC(0xDD, 0x73, 0x66, 0x73),
	BOM_SCSU(0x0E, 0xFE, 0xFF),
	BOM_BOCU_1(0xFB, 0xEE, 0x28),
	BOM_GB_18030(0x84, 0x31, 0x95, 0x33);
	
	byte[] bytes;	
	private BOM(int...bytes)
	{
		this.bytes = new byte[bytes.length];
		for(int i = 0; i < bytes.length; i++)
		{
			this.bytes[i] = (byte) bytes[i];
		}
	}
	
	public byte[] getBytes()
	{
		return this.bytes;
	}
		
	public String asString()
	{
		return new String(this.bytes);
	}
	
	public int length()
	{
		return this.bytes.length;
	}
}

Java:
package bom;

import java.io.IOException;

public class InvalidBOMException extends IOException
{
	private static final long serialVersionUID = -612753111805892956L;

	public InvalidBOMException(byte current, byte b)
	{
		super(String.format("Invalid BOM: expected 0x%02x, but read 0x%02x", b, current));
	}

}

Java:
package bom;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class BOMFileInputStream extends FileInputStream
{
	public BOMFileInputStream(File file) throws FileNotFoundException
	{
		super(file);
	}

	public BOMFileInputStream(FileDescriptor fileDescriptor) throws FileNotFoundException
	{
		super(fileDescriptor);
	}

	public BOMFileInputStream(String name) throws FileNotFoundException
	{
		super(name);
	}

	public void skip(BOM bom) throws IOException
	{
		byte[] bomBytes = bom.getBytes();
		for (int i = 0; i < bomBytes.length; i++)
		{
			int current = read();
			if (current == -1) // EOF
				return;

			byte currentByte = (byte) current;
			if (currentByte != bomBytes[i])
			{
				throw new InvalidBOMException(currentByte, bomBytes[i]);
			}
		}
	}
}
 
Ariol: Auch andere Streams können ja einen BOM+Text enthalten. Von dem her wäre es besser wenn es BOMInputStream heißen würde und von FilterInputStream erbt.
 
Dann eben so (ungetestet):

Java:
package bom;
 
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
 
public class BOMInputStream extends FilterInputStream
{
	private ByteOrderMark bom;
	
    public BOMInputStream(InputStream in) throws IOException
    {
        super(in);
        for(ByteOrderMark bom : ByteOrderMark.values())
        {
        	if(checkByteOrderMark(bom)) //BOM_NO_BOM will always work
        	{
        		this.bom = bom;
        		break;
        	}
        }
        
    }
    
    public ByteOrderMark getByteOrderMark()
    {
    	return this.bom;
    }

    private boolean checkByteOrderMark(ByteOrderMark bom) throws IOException
    {
    	if(bom == ByteOrderMark.BOM_NO_BOM)
    	{
    		reset();
    		return true;
    	}
    	
        byte[] bomBytes = bom.getBytes();
    	byte[] inBytes = new byte[bomBytes.length];
        
    	if(read(inBytes) != inBytes.length)
    	{
    		reset();
    		return false;
    	}
        
        if(!Arrays.equals(inBytes, bomBytes))
        {
    		reset();
    		return false;
    	}
        
        mark(Integer.MAX_VALUE);
        return true;
    }
}


Java:
package bom;
 
enum ByteOrderMark
{
	//4 bytes
    BOM_UTF_32_BE(0x00, 0x00, 0xFE, 0xFF),
    BOM_UTF_32_LE(0xFF, 0xFE, 0x00, 0x00),
    BOM_UTF_EBCDIC(0xDD, 0x73, 0x66, 0x73),
    BOM_GB_18030(0x84, 0x31, 0x95, 0x33),
    
    //3 bytes
    BOM_UTF_8(0xEF, 0xBB, 0xBF),
    BOM_UTF_7(0x2B, 0x2F, 0x76),
    BOM_UTF_1(0xF7, 0x64, 0x4C),
    BOM_SCSU(0x0E, 0xFE, 0xFF),
    BOM_BOCU_1(0xFB, 0xEE, 0x28),
    
    //2 bytes
    BOM_UTF_16_BE(0xFE, 0xFF),
    BOM_UTF_16_LE(0xFF, 0xFE),

    //no BOM
    BOM_NO_BOM();
    
    byte[] bytes;   
    private ByteOrderMark(int...bytes)
    {
        this.bytes = new byte[bytes.length];
        for(int i = 0; i < bytes.length; i++)
        {
            this.bytes[i] = (byte) bytes[i];
        }
    }
    
    public byte[] getBytes()
    {
        return this.bytes;
    }
        
    public String asString()
    {
        return new String(this.bytes);
    }
    
    public int length()
    {
        return this.bytes.length;
    }
}
 

Neue Themen


Zurück
Oben