Как просмотреть n-й символ в потоке чтения С#

Я создаю собственное средство чтения ввода для синтаксического анализатора на С# и пытаюсь получить n-й символ из средства чтения потока, не перемещая позицию потока, чтобы при многократном вызове функции я получал один и тот же вывод (например, встроенная функция просмотра).

В настоящее время я могу получить n-й элемент, как показано в коде ниже, однако при этом я перемещаю позицию потоков, и поэтому при каждом вызове я получаю другой символ.

private StreamReader _reader;
private int _peekChar = -1;

public InputReader(Stream inputStream)
{
    _reader = new StreamReader(inputStream);
}

//  Returns the next character in the input stream without consuming it
public char Peek()
{
    // Checks if character already peeked
    if (_peekChar == -1)
        _peekChar = _reader.Peek();

    return (char)_peekChar;
}

//  Returns the n th next character in the input stream without consuming it
public char Peek(int n)
{
    if (n <= 0)
        throw new ArgumentException("n must be a positive integer.");

    char[] buffer = new char[n];
    int bytesRead = _reader.Read(buffer, 0, n);

    if (bytesRead < n)
        throw new EndOfStreamException("Not enough characters in the stream to peek.");

    return buffer[n - 1];
}

Учитывая, что я вызываю Peek(1) 3 раза в потоке с "<HTML> <p> test <p/> <HTML/>", я ожидаю 3 вывода <, однако получаю <HT

🤔 А знаете ли вы, что...
C# является статически типизированным языком, что обеспечивает высокую безопасность и производительность кода.


1
72
3

Ответы:

Решено

Когда ты

_reader.Read(buffer, 0, n);

Затем позиция потока увеличивается.

Вам нужно переместить позицию потока обратно после чтения (если поток это поддерживает)

_reader.Seek(-bytesRead, SeekOrigin.End);


В этой ситуации я бы написал «PeekableStream», который поддерживает буфер, в который вы можете заглянуть. Он будет извлекать символы из буфера, если он доступен, а затем из исходного потока, когда буфер исчерпан. Буфер расширяется и заполняется на основе числа «n», переданного методу Peek.


Основываясь на ответе, предоставленном divinci, я обновил свою функцию до

public char Peek(int n)
{
    if (n < 0)
        throw new ArgumentException("k must be a positive integer.");

    long originalPosition = _reader.BaseStream.Position;
    char[] buffer = new char[n + 1];

    // Read characters up to the n-th position
    int readChars = _reader.Read(buffer, 0, n + 1);

    _reader.BaseStream.Seek(originalPosition, SeekOrigin.Begin);
    _reader.DiscardBufferedData();

    if (readChars < n + 1)
        throw new EndOfStreamException("Not enough characters in the stream to peek.");

    return buffer[n];
}

Затем у меня возникла проблема с возможностью циклического выполнения функции, и я обнаружил, что всякий раз, когда я использовал _reader.Peek(), она меняла положение потока. Я решил это, используя

long originalPosition = _reader.BaseStream.Position;
int nextChar = _reader.Peek();
_reader.BaseStream.Seek(originalPosition, SeekOrigin.Begin);

когда бы я ни позвонил _reader.Peek()