diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
index b772be488af1104ee72948ad489d3d39cbf4c767..3e0941dce29b4a1325dbe39163aad99d1a9b039a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
@@ -1061,7 +1061,7 @@ public class ConnectionSupport {
         if (conn instanceof BaseRConnection) {
             return (BaseRConnection) conn;
         } else if (conn instanceof DelegateReadRConnection) {
-            return ((DelegateReadRConnection) conn).base;
+            return ((DelegateRConnection) conn).base;
         } else {
             throw RInternalError.shouldNotReachHere();
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateRConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateRConnection.java
index 91359d131f9f9c7961f1d7325926c004ecbbf6be..0139c2e9a71b7867f954a047e6b0f8acc87d21a4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateRConnection.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateRConnection.java
@@ -30,6 +30,8 @@ import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.SeekableByteChannel;
 import java.nio.channels.WritableByteChannel;
 import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
 import java.util.ArrayList;
 import java.util.Objects;
 
@@ -52,6 +54,7 @@ import sun.nio.cs.StreamDecoder;
  */
 abstract class DelegateRConnection implements RConnection {
     protected final BaseRConnection base;
+    private StreamDecoder decoder;
 
     DelegateRConnection(BaseRConnection base) {
         this.base = Objects.requireNonNull(base);
@@ -275,34 +278,54 @@ abstract class DelegateRConnection implements RConnection {
         return new String(chars, 0, j);
     }
 
-    public static String readCharHelper(int nchars, ReadableByteChannel channel, boolean useBytes) throws IOException {
-        if (useBytes) {
-            ByteBuffer buf = ByteBuffer.allocate(nchars);
-            channel.read(buf);
-            int j = 0;
-            for (; j < buf.position(); j++) {
-                // strings end at 0
-                if (buf.get(j) == 0) {
-                    break;
-                }
+    /**
+     * Reads a specified number of single-byte characters.<br>
+     * <p>
+     * This method is meant to be used if R's function {@code readChar} is called with parameter
+     * {@code useBytes=TRUE}.
+     * </p>
+     *
+     * @param nchars The number of single-byte characters to read.
+     * @param channel The channel to read from (must not be {@code null}).
+     * @throws IOException
+     */
+    public static String readCharHelper(int nchars, ReadableByteChannel channel) throws IOException {
+        ByteBuffer buf = ByteBuffer.allocate(nchars);
+        channel.read(buf);
+        int j = 0;
+        for (; j < buf.position(); j++) {
+            // strings end at 0
+            if (buf.get(j) == 0) {
+                break;
             }
+        }
 
-            return new String(buf.array(), 0, j);
-        } else {
-            // we need a decoder
-            StreamDecoder decoder = StreamDecoder.forDecoder(channel, Charset.defaultCharset().newDecoder(), nchars);
-            char[] chars = new char[nchars];
-            decoder.read(chars);
-            int j = 0;
-            for (; j < chars.length; j++) {
-                // strings end at 0
-                if (chars[j] == 0) {
-                    break;
-                }
-            }
+        return new String(buf.array(), 0, j);
+    }
 
-            return new String(chars, 0, j);
+    /**
+     * Reads a specified number of characters (not bytes).<br>
+     * <p>
+     * This method is meant to be used if R's function {@code readChar} is called with parameter
+     * {@code useBytes=FALSE}.
+     * </p>
+     *
+     * @param nchars The number of characters to read.
+     * @param decoder The stream decoder to use (must not be {@code null}).
+     * @throws IOException
+     */
+    public static String readCharHelper(int nchars, StreamDecoder decoder) throws IOException {
+        // we need a decoder
+        char[] chars = new char[nchars];
+        decoder.read(chars);
+        int j = 0;
+        for (; j < chars.length; j++) {
+            // strings end at 0
+            if (chars[j] == 0) {
+                break;
+            }
         }
+        return new String(chars, 0, j);
     }
 
     /**
@@ -355,4 +378,15 @@ abstract class DelegateRConnection implements RConnection {
     public void pushBack(RAbstractStringVector lines, boolean addNewLine) {
         throw RInternalError.shouldNotReachHere();
     }
+
+    /**
+     * Creates the stream decoder on demand and returns it.
+     */
+    protected StreamDecoder getDecoder() throws IOException {
+        if (decoder == null) {
+            CharsetDecoder charsetEncoder = base.getEncoding().newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
+            decoder = StreamDecoder.forDecoder(getChannel(), charsetEncoder, -1);
+        }
+        return decoder;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadRConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadRConnection.java
index d3178472174f55b73b1e07317488141163935503..70a2bac8df5e6cc84b12f1b876f627b4d56a0b26 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadRConnection.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadRConnection.java
@@ -63,15 +63,23 @@ public abstract class DelegateReadRConnection extends DelegateRConnection {
 
     @Override
     public int getc() throws IOException {
-        tmp.clear();
-        int nread = getChannel().read(tmp);
-        tmp.rewind();
-        return nread > 0 ? tmp.get() : -1;
+        if (isTextMode()) {
+            return getDecoder().read();
+        } else {
+            tmp.clear();
+            int nread = getChannel().read(tmp);
+            tmp.rewind();
+            return nread > 0 ? tmp.get() : -1;
+        }
     }
 
     @Override
     public String readChar(int nchars, boolean useBytes) throws IOException {
-        return DelegateRConnection.readCharHelper(nchars, getChannel(), useBytes);
+        if (useBytes) {
+            return DelegateRConnection.readCharHelper(nchars, getChannel());
+        } else {
+            return DelegateRConnection.readCharHelper(nchars, getDecoder());
+        }
     }
 
     @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadWriteRConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadWriteRConnection.java
index 664f74183e24542fb646e76ad62438b369a4819d..011cf49d1b9256813e2f4151b61cf8565dd1f695 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadWriteRConnection.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadWriteRConnection.java
@@ -59,7 +59,11 @@ abstract class DelegateReadWriteRConnection extends DelegateRConnection {
 
     @Override
     public String readChar(int nchars, boolean useBytes) throws IOException {
-        return DelegateRConnection.readCharHelper(nchars, getChannel(), useBytes);
+        if (useBytes) {
+            return DelegateRConnection.readCharHelper(nchars, getChannel());
+        } else {
+            return DelegateRConnection.readCharHelper(nchars, getDecoder());
+        }
     }
 
     @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
index 761e543d6c239a960fb202b821e2792bb64a8354..341d12e3ba8fbdd49f07a197ab28d790f74acbb1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
@@ -43,12 +43,23 @@ public interface RConnection extends AutoCloseable {
     }
 
     /**
-     * Return the underlying input stream (for internal use).
+     * Return the underlying input stream (for internal use).<br>
+     * <p>
+     * <b>NOTE:</b> The connection may do some caching internally! Therefore, the behavior is
+     * undefined if you mix using the input stream directly and using the read methods of the
+     * connection.
+     * </p>
+     *
      */
     InputStream getInputStream() throws IOException;
 
     /**
-     * Return the underlying output stream (for internal use).
+     * Return the underlying output stream (for internal use).<br>
+     * <p>
+     * <b>NOTE:</b> The connection may do some caching internally! Therefore, the behavior is
+     * undefined if you mix using the output stream directly and using the write methods of the
+     * connection.
+     * </p>
      */
     OutputStream getOutputStream() throws IOException;