-2

I have a byte array which is filled by a serial port event and code is shown below:

private InputStream input = null; 
......
......
public void SerialEvent(SerialEvent se){
  if(se.getEventType == SerialPortEvent.DATA_AVAILABLE){
    int length = input.available();
    if(length > 0){
      byte[] array = new byte[length];
      int numBytes = input.read(array);
      String text = new String(array);
    }
  }
}

The variable text contains the below characters,

"\033[K", "\033[m",  "\033[H2J", "\033[6;1H" ,"\033[?12l", "\033[?25h", "\033[5i", "\033[4i", "\033i" and similar types..

As of now, I use String.replace to remove all these characters from the string.

I have tried new String(array , 'CharSet'); //Tried with all CharSet options but I couldn't able to remove those.

Is there any way where I can remove those characters without using replace method?

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
user3164187
  • 1,382
  • 3
  • 19
  • 50
  • Are you sure that the content you receive out of this port is a text-based at all? – Oleg Estekhin May 27 '14 at 10:20
  • @Oleg Estekhin I get [SquareSymbol] which is escape sequence \033 followed by whatever i have shown. e.g [Square][k like that. – user3164187 May 27 '14 at 10:22
  • `input.available()` is not a reliable way to check the number of bytes really available in the input stream, you should replace that part of code with the proper cycle over `InputStream.read(byte[],int,int)`, otherwise you are reading incomplete chunks of data of random length. – Oleg Estekhin May 27 '14 at 10:33
  • And additionally `input.read(array)` is not guaranteed to read the full array, so your current code first fails to determine the number of bytes really available in the stream and then fail to read even that wrongly-determined amount of bytes. – Oleg Estekhin May 27 '14 at 10:36
  • @OlegEstekhin input.read(array, ?, ?) can u please give me a small example.. Or a link to start with. – user3164187 May 27 '14 at 10:39
  • You can start with [Java tutorials](http://docs.oracle.com/javase/tutorial/essential/io/) and [this question](http://stackoverflow.com/questions/2163644/in-java-how-can-i-convert-an-inputstream-into-a-byte-array-byte) and links from it. – Oleg Estekhin May 27 '14 at 10:42
  • @OlegEstekhin I have read but i dont understand how `input.available` can be replaces with `input.read(byte[],int,int);` to get the number of bytes.Can you please help with that. – user3164187 May 27 '14 at 12:09

1 Answers1

0

I gave a unsatisfying answer, thanks to @OlegEstekhin for pointing that out. As noone else answered yet, and a solution is not a two-liner, here it goes.

Make a wrapping InputStream that throws away escape sequences. I have used a PushbackInputStream, where a partial sequence skipped, may still be pushed back for reading first. Here a FilterInputStream would suffice.

public class EscapeRemovingInputStream extends PushbackInputStream {

    public static void main(String[] args) {
        String s = "\u001B[kHello \u001B[H12JWorld!";
        byte[] buf = s.getBytes(StandardCharsets.ISO_8859_1);
        ByteArrayInputStream bais = new ByteArrayInputStream(buf);
        EscapeRemovingInputStream bin = new EscapeRemovingInputStream(bais);
        try (InputStreamReader in = new InputStreamReader(bin,
                StandardCharsets.ISO_8859_1)) {
            int c;
            while ((c = in.read()) != -1) {
                System.out.print((char) c);
            }
            System.out.println();
        } catch (IOException ex) {
            Logger.getLogger(EscapeRemovingInputStream.class.getName()).log(
                Level.SEVERE, null, ex);
        }
    }

    private static final Pattern ESCAPE_PATTERN = Pattern.compile(
        "\u001B\\[(k|m|H\\d+J|\\d+:\\d+H|\\?\\d+\\w|\\d*i)");
    private static final int MAX_ESCAPE_LENGTH = 20;

    private final byte[] escapeSequence = new byte[MAX_ESCAPE_LENGTH];
    private int escapeLength = 0;
    private boolean eof = false;

    public EscapeRemovingInputStream(InputStream in) {
        this(in, MAX_ESCAPE_LENGTH);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        for (int i = 0; i < len; ++i) {
            int c = read();
            if (c == -1) {
                return i == 0 ? -1 : i;
            }
            b[off + i] = (byte) c;
        }
        return len;
    }

    @Override
    public int read() throws IOException {
        int c = eof ? -1 : super.read();
        if (c == -1) { // Throw away a trailing half escape sequence.
            eof = true;
            return c;
        }
        if (escapeLength == 0 && c != 0x1B) {
            return c;
        } else {
            escapeSequence[escapeLength] = (byte) c;
            ++escapeLength;
            String esc = new String(escapeSequence, 0, escapeLength,
                    StandardCharsets.ISO_8859_1);
            if (ESCAPE_PATTERN.matcher(esc).matches()) {
                escapeLength = 0;
            } else if (escapeLength == MAX_ESCAPE_LENGTH) {
                escapeLength = 0;
                unread(escapeSequence);
                return super.read(); // No longer registering the escape
            }
            return read();
        }
    }

}
  • User calls EscapeRemovingInputStream.read
  • this read may call some read's itself to fill an byte buffer escapeSequence
  • (a push-back may be done calling unread)
  • the original read returns.

The recognition of an escape sequence seems grammatical: command letter, numerical argument(s). Hence I use a regular expression.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138