(defun custom-read-line (stream &key (max 512)) (let ((line (make-array max :element-type 'character :adjustable t :fill-pointer 0))) (flet ((add-char (c) (declare (type character c)) (vector-push c line)) (finalize-line () (let ((len (length line))) (when (and (> len 0) (char= #\Return (aref line (1- len)))) (vector-pop line))) line)) (loop do (let ( ;; No way to determine invalid octet values with old ECL, ;; Return an unknown character code (c #+old-ecl(handler-case (read-char stream) (simple-error () #\UFFFD)) ;; SBCL provides invalid octets which we can import and ;; then issue an ATTEMPT-RESYNC restart to resume #+sbcl(handler-bind ((sb-int:stream-decoding-error #'(lambda (e) ;; Treat invalid UTF-8 octets as ;; ISO-8859 characters. (mapcar #'(lambda (c) (when (> c 127) (add-char (code-char c)))) (sb-int:character-decoding-error-octets e)) (invoke-restart 'sb-int:attempt-resync)))) (read-char stream)) ;; Test with new ECL #+ecl(handler-bind ((si::stream-decoding-error ; Internal #'(lambda (e) (mapcar #'(lambda (c) (format t "~%~A~%" c) (when (> c 127) ;; Never happens (add-char (code-char c)))) ;; Not advertized interface? (si:character-decoding-error-octets e)) ;; No restart function? ;; USE-VALUE not found, ATTEMPT-RESYNC ;; either... ;(invoke-restart 'si:use-value #\?) ))) (read-char stream))) ) (when (char= #\Newline c) (return (values (finalize-line) t))) (add-char c)))))) (defun test () (with-open-file (stream "/tmp/InvalidUTF8.txt") (loop do (let ((line (handler-case (custom-read-line stream) (end-of-file () (loop-finish))))) (format t "~A~%" line)))))