Chars größer 127 mit FilterInputStream aussortieren

barthimaeus

Mitglied
Hallo zusammen!

Ich arbeite zZ. an einen kleinen Mud Client, um mir einige Programmierkonzepte näher zu bringen, die ich bisher umschifft habe
(MVC/Streams). In dieser Frage soll es jedoch nur um den FilterInputStream gehen, da ich die anderen Probleme bisher bereits lösen konnte.

Da die Kommunikation mit einem Mud im Regelfall über Telnet erfolgt habe ich eine Socketverbindung zu dem Server ("mg.mud.de") auf Port23 geöffnet und mir die Input/Output Streams geholt. Der InputStream wird dann über InputStreamReader und BufferedReader in einem eigenen Thread in einen String message ausgelesen und vom Datenmodell (s. mvc) überwacht.

Soweit so gut. Nun ist es allerdings so, das eine Telnet Verbindung nicht nur Daten, sondern auch Metainformationen und Anweisungen an den Client überträgt (Negotiatiate, etc.). Zu diesem Zweck verwendet Telnet als Zeichensatz idR. 7Bit ASCII, sodass mit einem Byte auch noch 128 mögliche Befehle übertragen werden können. Diese möchte ich nun mit einem FilterInputStream zwischen Socket und InputStreamReader rausfiltern, sodass sie mich in der späteren Darstellung der Daten nicht stören.

Hier meine bisherige Klasse
Code:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package view;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 *
 * @author barthimaeus
 */
public class CommandFilter extends FilterInputStream {

    public CommandFilter(InputStream in){
        super(in);
    }

    @Override
    public int read() throws IOException{
        int temp = 0;
        do {
            temp = in.read();
            if (temp ==-1){
                return -1;
            }
        }while (temp>127);

        return temp;
    }
    
    @Override
    public int read(byte[] b) throws IOException{
        int temp;
        int i;
        for (i = 0; i<b.length;){
            if((temp = this.read())!=-1){
                b[i]=(byte)temp;
                i++;
            }
            else {
                if (i==0){
                    return -1;
                }
                else {
                    break;
                }
            }
        }
        return i;
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException{
        int temp;
        int i;
        for (i = off; i<off+len;){
            if((temp = this.read())!=-1){
                b[i]=(byte)temp;
                i++;
            }
            else {
                if (i==off){
                    return -1;
                }
                else {
                    break;
                }
            }
        }
        return i-off+1;
    }


}

Leider funktioniert dies jedoch nicht, denn aus irgendeinem Grund lässt der Filter keinerlei Bytes mehr durch.
Ich hoffe ihr wisst weiter. Vielen Dank schonmal
 
Zuletzt bearbeitet:
S

SlaterB

Gast
schau dir doch genau an, was ankommt und was davon zu filtern ist und was nicht

so wie es jetzt ist kann es schonmal nicht funktionieren,
weil jedes read() einen byte liefert, welcher nur einen Wertebereich von -128 bis +127 hat,
entweder willst du davon alles >= 0 filtern oder besser die bytes bzw. den ganzen Strom erst noch in einem bestimmten Encoding als Text interpretieren und dann die Chars nach ASCII-Code filtern?
 

Ark

Top Contributor
Bist du dir sicher, dass in dem einzulesenden Datenstrom auch Bytes vorkommen, die das Filter passieren können?

EDIT@SlaterB: Ich meine doch, dass [c]read()[/c] Werte zwischen 0 und 255 inklusive bzw. -1 zurückgibt. Insofern sehe ich da gerade weniger ein Problem ...

Ark
 

Marco13

Top Contributor
Also es scheint eigentlich zu funktionieren, bei so einem Test
Java:
public class CommandFilterTest
{
    public static void main(String args[]) throws Exception
    {
        byte a[] = new byte[]
        {
            (byte)1,
            (byte)2,
            (byte)3,
            (byte)126,
            (byte)127,
            (byte)128,
            (byte)129,
            (byte)3,
            (byte)2,
            (byte)1,
        };
        InputStream bais = new ByteArrayInputStream(a);
        InputStream is = new CommandFilter(bais);

        byte b[] = new byte[5];
        while (true)
        {
            int n = is.read(b);
            if (n==-1)
            {
                break;
            }
            for (int i=0; i<n; i++)
            {
                System.out.println(b[i]);
            }
        }

        /*
        while (true)
        {
            int b = is.read();
            if (b == -1)
            {
                break;
            }
            System.out.println(b);
        }
        */
    }



}


Ansonsten: Die erste Read-Methode sollte so implementiert sein:
Java:
    @Override
    public int read(byte[] b) throws IOException
    {
        return read(b, 0, b.length);
    }

Und die zweite... Die read-Methode, die einzelne Zeichnen liest, ist bei größeren Datenmengen gähnend langsam. FALLS das ganze effizient sein soll, wäre es besser, grob mach diesem Schema vorzugehen:
Code:
int read(byte b[], int off, int len)
{
    int totalRead = 0;
    while (true)
    {
        int read = in.read(b,off,len);
        if (read==-1) return totalRead (oder -1);
        int valid = schmeißUngültigeEinträgeAusDemArray(b,off,len);
        totalRead += valid;
        passeOffUndLenAn();
    }
    return totalRead;
}

Das tatsächlich auszuimplementieren ist etwas frickeliger als ich zuerst dachte, ein aus Zeitmangel nur schnell hingeschriebener Ansatz wäre grob sowas wie
Java:
    private int compact(byte b[], int off, int len)
    {
        int readIndex = off;
        for (int i=off; i<off+len; i++)
        {
            while (b[readIndex] < 0)
            {
                readIndex++;
                if (readIndex >= b.length)
                {
                    return i-off;
                }
            }
            if (readIndex < b.length)
            {
                b[i] = b[readIndex];
                readIndex++;
            }
            if (readIndex >= b.length)
            {
                return i-off+1;
            }
        }
        return len;
    }


    @Override
    public int read(byte[] b, int off, int len) throws IOException
    {
        int currentOff = off;
        int currentLen = len;
        int read = -1;
        while (true)
        {
            int n = in.read(b, currentOff, currentLen);
            if (n == -1)
            {
                break;
            }

            int valid = compact(b, currentOff, n);
            if (read == -1)
            {
                read = 0;
            }
            read += valid;
            currentOff += valid;
            currentLen -= valid;
            if (currentLen == 0)
            {
                break;
            }
        }
        return read;
    }
Aber das ist NICHT richtig getestet oder verifiziert, und ziemlich unschön und unüberlegt...
 

barthimaeus

Mitglied
Vielen dank schonmal für die zahlreichen schnellen Antworten. Ich werde mich jetzt erstmal mit der Analyse und Umsetztung eurer Ratschläge befassen und melde mich dann zurück ;)

danke
 

barthimaeus

Mitglied
So, nach einer kleinen unfreiwilligen Pause habe ich mich jetzt nochmal an den Code gesetzt und eine andere Variante der Funktion geschrieben, wie verlangt, nicht die Bytes einzeln einliest und weiterverarbeitet, sondern ebenfalls mit einem Puffer arbeitet.
Ich glaube diese Lösung ist durch die Rekursion sogar recht elegant:

Code:
public int read(byte[] b, int off, int len) throws IOException{
        int total = in.read(b, off, len);
        //System.out.println(total);
        if (total == -1){
            return -1;
        }
        Vector<Byte> vec = new Vector<Byte>();
        for (int i = off; i<off+total; i++){
            if (b[i]>=0){
                vec.add(b[i]);
                //System.out.println(b[i]);
            }
        }
        int readsofar = vec.size();
        //System.out.println(readsofar+"\n");
        
        for (int i=off; i<off+len;i++){
            if (i<off+readsofar){
                b[i]=vec.firstElement();
                vec.remove(vec.firstElement());
            }
            else {
                b[i]=0;
            }
            
        }
        if (readsofar<len){
            total = this.read(b, off+readsofar, len-readsofar);
            if (total > -1){
                readsofar += total;
            }
        }
        return readsofar;
    }

Was haltet ihr davon? Könnte man die Funktion noch weiter verbessern?
 

Marco13

Top Contributor
Eine Rekursion habe ich da jetzt nicht gesehen, aber es ist zu vermuten, dass ein Vector<Byte> den Geschweindigkeitsvorteil durch das nicht-einzelne Lesen wieder auffrißt...
 

barthimaeus

Mitglied
Der Rekursionsschritt liegt hier:
Code:
total = this.read(b, off+readsofar, len-readsofar);
Der Vektor lässt sich vermutlich auch noch durch ein Array ersetzen...
 

Marco13

Top Contributor
OK, jetzt sehe ich das auch *augenreib* Aber ... wozu ist genau der Vector da? Es wird doch nur das, was im Array ist, in den Vector kopiert.. und danach wieder zurück?! (Oder ist das jetzt auch wieder ein Zeichen von Koffeinmangel?)
 

barthimaeus

Mitglied
Der Vector dient zZ. nur als dynamischer Zwischenspeicher für die zulässigen Elemente. Vermutlich lässt es sich auch "in place" lösen. Mal gucken ;)
Eine Alternative dazu wäre glaube ich eine LinkedList, die eigenentlich auch recht effizient sein müsste, da sie ja intern keinen Array (wie der Vektor) verwendet und daher die Größenänderungen kein Zeitproblem darstellen. Da auch immer nur das erste Element der Liste gelesen wird, tritt das Performanceproblem der LinkedList beim durchlaufen zum n-ten Element nicht auf. Eigentlich optimal.
 

Marco13

Top Contributor
Es sollte sich definitiv in-Place lösen lassen (und es kann gut sein, dass sich das rekursiv etwas eleganter lösen läßt). Aber ich meinte eigentlich, dass die größte potentielle Rechenzeitverschwendung aus der Verwendung einer Liste an sich, und insbesondere aus dem Boxing/Unboxing von byte nach Byte besteht.

Rechenzeitverschwendung, weil die größte tatsächliche Zeitverschwendung der Versuch sein könnte, sich für dieses Problem eine Lösung zu überlegen, obwohl dieses Lesen und Filtern vielleicht gar nicht zeitkritisch ist. Also, wenn man nur read() verwendet, ist das ganze ein trivialer 3-Zeiler. Wenn man read(byte[]) verwenden will, wird's frickelig. Wer weiß, ob sich der Aufwand lohnt...
 

Ähnliche Java Themen

Neue Themen


Oben