utf-8 codec can't decode strings randomly
I think your code is breaking because (as stated by @wilks) UTF has a variable length of bytes, not always just 1. Assuming your file format has UTF-8 null-terminated strings inside a larger block of binary data we can do something like the following little test program you can run in iPython or your favorite ide. It has a list of valid, null-terminated utf-8 bytes with cruft at the end.

bob=b'\xe2\x9c\x93 means check\x00\xc0'

chars = bytearray()

for c in bob:
    if c == 0: # c is int so does not match b'\x00'
        break
    chars.append(c)

print(chars.decode('utf-8'))

And here is the (untested) changes I would make to your read_string function:
def read_string(f):
    chars = bytearray() # avoid lists
    i = 0
    while True:
        c = f.read(1)
        i += 1
        if (c == b'\x00' ): # c is bytes() 
            return (chars.decode('utf-8') )
        chars.append( c[0] ) # stick another bare byte onto chars