Document trailing newline behavior for ReadLine and ReadAllLines/ReadLines APIs#12493
Document trailing newline behavior for ReadLine and ReadAllLines/ReadLines APIs#12493
Conversation
… TextReader.ReadLine, StringReader.ReadLine, File.ReadAllLines, and File.ReadLines Agent-Logs-Url: https://github.com/dotnet/dotnet-api-docs/sessions/26046a78-c7a0-46a9-bccb-394eb343d453 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…eadLine docs Agent-Logs-Url: https://github.com/dotnet/dotnet-api-docs/sessions/26046a78-c7a0-46a9-bccb-394eb343d453 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-io |
…ocumentation Agent-Logs-Url: https://github.com/dotnet/dotnet-api-docs/sessions/916a4b61-9a5e-44f6-a396-cf4dfe84056d Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com>
|
I will work on the merge conflicts... |
There was a problem hiding this comment.
Pull request overview
This PR documents a subtle but important behavior of the .NET line-reading APIs: a trailing newline at the end of the input does not produce an extra empty string/line in the returned results, reducing confusion for developers reading streams, strings, and files.
Changes:
- Added a note to
ReadLineremarks forTextReader,StringReader, andStreamReaderclarifying trailing-newline behavior. - Added the same clarification to
File.ReadAllLines(both overloads) andFile.ReadLines(both overloads), including examples.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| xml/System.IO/TextReader.xml | Adds trailing-newline behavior note to TextReader.ReadLine remarks. |
| xml/System.IO/StringReader.xml | Adds trailing-newline behavior note to StringReader.ReadLine remarks and removes extra blank lines before Examples. |
| xml/System.IO/StreamReader.xml | Adds trailing-newline behavior note to StreamReader.ReadLine remarks. |
| xml/System.IO/File.xml | Adds trailing-newline behavior notes to ReadAllLines and ReadLines remarks (including encoding overloads), plus minor formatting adjustments around Examples. |
|
|
||
| This method uses <xref:System.Text.Encoding.UTF8*> for the encoding value. | ||
|
|
||
| A line is defined as a sequence of characters followed by a carriage return ('\r'), a line feed ('\n'), or a carriage return immediately followed by a line feed. If the file ends with a newline sequence, no additional empty line is returned. For example, a file containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as a file containing `"line1\nline2"`. |
There was a problem hiding this comment.
The new line definition implies that a line is always terminated by a CR/LF sequence, but File.ReadLines also returns the final line when the file ends without a trailing newline. Consider updating the definition to include end-of-file/end-of-stream as a valid line terminator.
| A line is defined as a sequence of characters followed by a carriage return ('\r'), a line feed ('\n'), or a carriage return immediately followed by a line feed. If the file ends with a newline sequence, no additional empty line is returned. For example, a file containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as a file containing `"line1\nline2"`. | |
| A line is defined as a sequence of characters followed by a carriage return ('\r'), a line feed ('\n'), or a carriage return immediately followed by a line feed, or as the final sequence of characters in the file or stream if it ends without a newline sequence. If the file ends with a newline sequence, no additional empty line is returned. For example, a file containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as a file containing `"line1\nline2"`. |
| - Write the returned collection of lines to a file with the <xref:System.IO.File.WriteAllLines(System.String,System.Collections.Generic.IEnumerable{System.String},System.Text.Encoding)?displayProperty=nameWithType> method, or append them to an existing file with the <xref:System.IO.File.AppendAllLines(System.String,System.Collections.Generic.IEnumerable{System.String},System.Text.Encoding)?displayProperty=nameWithType> method. | ||
| - Create an immediately populated instance of a collection that takes an <xref:System.Collections.Generic.IEnumerable`1> collection of strings for its constructor, such as a <xref:System.Collections.Generic.IList`1> or a <xref:System.Collections.Generic.Queue`1>. | ||
|
|
||
| A line is defined as a sequence of characters followed by a carriage return ('\r'), a line feed ('\n'), or a carriage return immediately followed by a line feed. If the file ends with a newline sequence, no additional empty line is returned. For example, a file containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as a file containing `"line1\nline2"`. |
There was a problem hiding this comment.
Two issues in this added paragraph: (1) it starts with extra leading whitespace compared to the surrounding Markdown in this CDATA block, and (2) the line definition should include end-of-file/end-of-stream as a valid terminator (the last line is returned even without a trailing newline).
| A line is defined as a sequence of characters followed by a carriage return ('\r'), a line feed ('\n'), or a carriage return immediately followed by a line feed. If the file ends with a newline sequence, no additional empty line is returned. For example, a file containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as a file containing `"line1\nline2"`. | |
| A line is defined as a sequence of characters terminated by a carriage return ('\r'), a line feed ('\n'), a carriage return immediately followed by a line feed, or the end of the file or stream. If the file ends with a newline sequence, no additional empty line is returned. For example, a file containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as a file containing `"line1\nline2"`. |
| A line is defined as a sequence of characters followed by a carriage return (0x000d), a line feed (0x000a), a carriage return followed by a line feed, <xref:System.Environment.NewLine*?displayProperty=nameWithType>, or the end-of-stream marker. The string that is returned does not contain the terminating carriage return or line feed. The return value is `null` if the end of the input stream has been reached. | ||
|
|
||
| If the stream ends with a newline sequence, no additional empty line is returned. For example, a stream containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as a stream containing `"line1\nline2"`. | ||
|
|
||
| If the method throws an <xref:System.OutOfMemoryException> exception, the reader's position in the underlying <xref:System.IO.Stream> is advanced by the number of characters the method was able to read, but the characters that were already read into the internal <xref:System.IO.TextReader.ReadLine*> buffer are discarded. Because the position of the reader in the stream cannot be changed, the characters that were already read are unrecoverable and can be accessed only by reinitializing the <xref:System.IO.TextReader> object. If the initial position within the stream is unknown or the stream does not support seeking, the underlying <xref:System.IO.Stream> also needs to be reinitialized. |
There was a problem hiding this comment.
In this TextReader docs block, the new note refers to a "stream", but TextReader isn't necessarily backed by a stream (for example, StringReader). Consider rephrasing to refer to the reader's underlying source/input instead of a stream to avoid implying a stream is required.
| A line is defined as a sequence of characters followed by a carriage return (0x000d), a line feed (0x000a), a carriage return followed by a line feed, <xref:System.Environment.NewLine*?displayProperty=nameWithType>, or the end-of-stream marker. The string that is returned does not contain the terminating carriage return or line feed. The return value is `null` if the end of the input stream has been reached. | |
| If the stream ends with a newline sequence, no additional empty line is returned. For example, a stream containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as a stream containing `"line1\nline2"`. | |
| If the method throws an <xref:System.OutOfMemoryException> exception, the reader's position in the underlying <xref:System.IO.Stream> is advanced by the number of characters the method was able to read, but the characters that were already read into the internal <xref:System.IO.TextReader.ReadLine*> buffer are discarded. Because the position of the reader in the stream cannot be changed, the characters that were already read are unrecoverable and can be accessed only by reinitializing the <xref:System.IO.TextReader> object. If the initial position within the stream is unknown or the stream does not support seeking, the underlying <xref:System.IO.Stream> also needs to be reinitialized. | |
| A line is defined as a sequence of characters followed by a carriage return (0x000d), a line feed (0x000a), a carriage return followed by a line feed, <xref:System.Environment.NewLine*?displayProperty=nameWithType>, or the end of the reader's input. The string that is returned does not contain the terminating carriage return or line feed. The return value is `null` if the end of the input has been reached. | |
| If the input ends with a newline sequence, no additional empty line is returned. For example, input containing `"line1\nline2\n"` produces the same two lines (`"line1"` and `"line2"`) as input containing `"line1\nline2"`. | |
| If the method throws an <xref:System.OutOfMemoryException> exception, the reader's position in its underlying source is advanced by the number of characters the method was able to read, but the characters that were already read into the internal <xref:System.IO.TextReader.ReadLine*> buffer are discarded. Because the reader's position in the underlying source cannot be reset, the characters that were already read are unrecoverable and can be accessed only by reinitializing the <xref:System.IO.TextReader> object. If the initial position within the underlying source is unknown or the source does not support repositioning, the underlying source also needs to be reinitialized. |
A trailing newline at the end of a stream or file does not produce a final empty string —
"foo\nbar\n"and"foo\nbar"both yield the same two lines. This behavior is common but undocumented, making it a source of confusion.Affected APIs
Added a note to the
## Remarkssection of each:StreamReader.ReadLineTextReader.ReadLineStringReader.ReadLineFile.ReadAllLines(string)/File.ReadAllLines(string, Encoding)File.ReadLines(string)/File.ReadLines(string, Encoding)Example note added (StreamReader.ReadLine)