replacing text in a printstream

2.7k Views Asked by At

Is it possible to have a regexp replace in a printstream?

I have a piece of code that logs all text that is shown in my console windows but it also logs ANSI escape codes.

I have found this regexp "s:\x1B\[[0-9;]*[mK]::g" to remove them but that only works with strings. Is there a way to apply a regex replace to a constant stream of strings and filter out the ANSI escape codes?

If possible, dumb it down as much as possible, I am still a newbie when it comes to programming, I am just building upon a already program.

EDIT:

I have this code which I found somewhere else on stack overflow, this allows me to stream to a logfile and to the console at the same time.

This is what I use and then I set the out to tee after this.

Logging tee = new Logging(file, System.out);

.

package com.md_5.mc.chat;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

public class Logging extends PrintStream
{
  private final PrintStream second;

  public Logging(OutputStream main, PrintStream second)
  {
    super(main);
    this.second = second;
  }

  public void close()
  {
    super.close();
  }

  public void flush()
  {
    super.flush();
    this.second.flush();
  }

  public void write(byte[] buf, int off, int len)
  {
    super.write(buf, off, len);
    this.second.write(buf, off, len);
  }

  public void write(int b)
  {
    super.write(b);
    this.second.write(b);
  }

  public void write(byte[] b) throws IOException
  {
    super.write(b);
    this.second.write(b);
  }
}
3

There are 3 best solutions below

3
On

You could subclass the print stream in question and perform your regexp replacing prior to calling the appropriate super method? E.g.

public void ExampleStream extends PrintStream {

  @Override
  public void print(String s) {
    super(s.replaceAll(ANSI_PATTERN,""));
  }
}
1
On

Create create a subclass of FilterOutputStream, say RegexOutputStream. This class should buffer all data written to it (from the different write(...) methods). In the flush() method, it should apply the regex and then write the result to the underlying OutputStream.

Next, instantiate the PrintWriter to write to the RegexOutputStream. This way you don't need to alter the behaviour of the PrintWriter class. In case you don't want the filtering anymore, you can just take the RegexOutStream out of the chain, and everything will work again.

Note that, depending on how you use the PrintWriter, this might cause the RegexOutputStreams buffer to get quite big. If you create the PrintWriter to autoflush, it will flush after every line and after every byte array. See its JavaDoc for details.

3
On

I think that the code in Logging class is not a good approach (at least as it is):

  • If you have access to the PrintStream source code you might find that the methods currently redefined might not being used at all: the PrintStream#print(...) methods delegate on textOut#write(...) (not on the redefined OutputStream#write(...) ).
  • Therefore, you should redefine the print(String) and print(char[]) methods in order to effectively filter the output.
  • There are a few examples of redefined methods in the answers (including further down on this one).

Alternatively, if you just want a PrintStream that filters out the ANSI codes (as I originally understood), then it would be more convenient to implement it on a FilterOutputStream (as mthmulders suggests, as you will have to redefine fewer stuff and will be easier to re-use):

  • Make a copy of BufferedOutputStream class. Name it however you prefer. (E.g. TrimAnsiBufferedStream)
  • Then redefine de flushBuffer() method:

    private void flushBuffer() throws IOException {
        if (count > 0) {
            String s = new String(buf, 0, count); // Uses system encoding.
            s.replaceAll(ANSI_PATTERN, "");
            out.write(s.getBytes());
            count = 0;
        }
    }
    
  • When you need to instantiate a PrintStream that replaces ANSI, invoke new PrintStream(new TrimAnsiBufferedStream(nestedStream)).

  • This is probably not bullet-proof (e.g. whatever may happen with encoding configuration, or if buffer size is not big enough, or flushing options in printstream), but I won't overcomplicate it.

By the way. Welcome kukelekuuk00. Just be sure to read the FAQ and feedback on the answers (we care about you, please reciprocate).