You don't want plain read for something like this. It is built to read Scheme/Racket syntax, not arbitrary data. Instead, you probably want string->list, which splits a string into a list of characters.
(string->list "(expression here)")
; => '(#\( #\e #\x #\p #\r #\e #\s #\s #\i #\o #\n #\space #\h #\e #\r #\e #\))
Perhaps you don't want to read the whole string at once, though? There does exist a read-char function, as you include in your post. There are lots of ways to loop in Racket. You can use recursion, or you can use Racket's plethora of for loop forms.
Still, if you have an input port and you just want to loop over all the characters, well, there's an easy way to do that, too! You can use the handy in-input-port-chars sequence with for loops like this:
(define expression (open-input-string "(expression here)"))
(for ([c (in-input-port-chars expression)])
(display c))
; => (expression here)