I use the following construct:
while IFS= read -r LINE || [[ -n "$LINE" ]]; do
echo "$LINE"
done
It works with pretty much anything except null characters in the input:
- Files that start or end with blank lines
- Lines that start or end with whitespace
- Files that don’t have a terminating newline