Как ограничить количество символов, считанных StreamReader. ReadLine () в.NET?

Вот реализация AsyncCommand, которую я создал для этого пакета NuGet: AsyncAwaitBestPractices.MVVM .

Эта реализация была вдохновлена ​​публикацией в блоге @John Thiriet , «AsyncCommand» .

using System;
using System.Threading.Tasks;
using System.Windows.Input;

namespace AsyncAwaitBestPractices.MVVM
{
    /// 
    /// An implmentation of IAsyncCommand. Allows Commands to safely be used asynchronously with Task.
    /// 
    public sealed class AsyncCommand : IAsyncCommand
    {
        #region Constant Fields
        readonly Func _execute;
        readonly Func _canExecute;
        readonly Action _onException;
        readonly bool _continueOnCapturedContext;
        readonly WeakEventManager _weakEventManager = new WeakEventManager();
        #endregion

        #region Constructors
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The Function executed when Execute or ExecuteAysnc is called. This does not check canExecute before executing and will execute even if canExecute is false
        /// The Function that verifies whether or not AsyncCommand should execute.
        /// If an exception is thrown in the Task, onException will execute. If onException is null, the exception will be re-thrown
        /// If set to true continue on captured context; this will ensure that the Synchronization Context returns to the calling thread. If set to false continue on a different context; this will allow the Synchronization Context to continue on a different thread
        public AsyncCommand(Func execute,
                            Func canExecute = null,
                            Action onException = null,
                            bool continueOnCapturedContext = true)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute), $"{nameof(execute)} cannot be null");
            _canExecute = canExecute ?? (_ => true);
            _onException = onException;
            _continueOnCapturedContext = continueOnCapturedContext;
        }
        #endregion

        #region Events
        /// 
        /// Occurs when changes occur that affect whether or not the command should execute
        /// 
        public event EventHandler CanExecuteChanged
        {
            add => _weakEventManager.AddEventHandler(value);
            remove => _weakEventManager.RemoveEventHandler(value);
        }
        #endregion

        #region Methods
        /// 
        /// Determines whether the command can execute in its current state
        /// 
        /// true, if this command can be executed; otherwise, false.
        /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
        public bool CanExecute(object parameter) => _canExecute(parameter);

        /// 
        /// Raises the CanExecuteChanged event.
        /// 
        public void RaiseCanExecuteChanged() => _weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(CanExecuteChanged));

        /// 
        /// Executes the Command as a Task
        /// 
        /// The executed Task
        /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
        public Task ExecuteAsync(T parameter) => _execute(parameter);

        void ICommand.Execute(object parameter)
        {
            if (parameter is T validParameter)
                ExecuteAsync(validParameter).SafeFireAndForget(_continueOnCapturedContext, _onException);
            else if (parameter is null && !typeof(T).IsValueType)
                ExecuteAsync((T)parameter).SafeFireAndForget(_continueOnCapturedContext, _onException);
            else
                throw new InvalidCommandParameterException(typeof(T), parameter.GetType());
        }
        #endregion
    }

    /// 
    /// An implmentation of IAsyncCommand. Allows Commands to safely be used asynchronously with Task.
    /// 
    public sealed class AsyncCommand : IAsyncCommand
    {
        #region Constant Fields
        readonly Func _execute;
        readonly Func _canExecute;
        readonly Action _onException;
        readonly bool _continueOnCapturedContext;
        readonly WeakEventManager _weakEventManager = new WeakEventManager();
        #endregion

        #region Constructors
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The Function executed when Execute or ExecuteAysnc is called. This does not check canExecute before executing and will execute even if canExecute is false
        /// The Function that verifies whether or not AsyncCommand should execute.
        /// If an exception is thrown in the Task, onException will execute. If onException is null, the exception will be re-thrown
        /// If set to true continue on captured context; this will ensure that the Synchronization Context returns to the calling thread. If set to false continue on a different context; this will allow the Synchronization Context to continue on a different thread
        public AsyncCommand(Func execute,
                            Func canExecute = null,
                            Action onException = null,
                            bool continueOnCapturedContext = true)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute), $"{nameof(execute)} cannot be null");
            _canExecute = canExecute ?? (_ => true);
            _onException = onException;
            _continueOnCapturedContext = continueOnCapturedContext;
        }
        #endregion

        #region Events
        /// 
        /// Occurs when changes occur that affect whether or not the command should execute
        /// 
        public event EventHandler CanExecuteChanged
        {
            add => _weakEventManager.AddEventHandler(value);
            remove => _weakEventManager.RemoveEventHandler(value);
        }
        #endregion

        #region Methods
        /// 
        /// Determines whether the command can execute in its current state
        /// 
        /// true, if this command can be executed; otherwise, false.
        /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
        public bool CanExecute(object parameter) => _canExecute(parameter);

        /// 
        /// Raises the CanExecuteChanged event.
        /// 
        public void RaiseCanExecuteChanged() => _weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(CanExecuteChanged));

        /// 
        /// Executes the Command as a Task
        /// 
        /// The executed Task
        public Task ExecuteAsync() => _execute();

        void ICommand.Execute(object parameter) => _execute().SafeFireAndForget(_continueOnCapturedContext, _onException);
        #endregion
    }

    /// 
    /// Extension methods for System.Threading.Tasks.Task
    /// 
    public static class TaskExtensions
    {
        /// 
        /// Safely execute the Task without waiting for it to complete before moving to the next line of code; commonly known as "Fire And Forget". Inspired by John Thiriet's blog post, "Removing Async Void": https://johnthiriet.com/removing-async-void/.
        /// 
        /// Task.
        /// If set to true continue on captured context; this will ensure that the Synchronization Context returns to the calling thread. If set to false continue on a different context; this will allow the Synchronization Context to continue on a different thread
        /// If an exception is thrown in the Task, onException will execute. If onException is null, the exception will be re-thrown
        #pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void
        public static async void SafeFireAndForget(this System.Threading.Tasks.Task task, bool continueOnCapturedContext = true, System.Action onException = null)
        #pragma warning restore RECS0165 // Asynchronous methods should return a Task instead of void
        {
            try
            {
                await task.ConfigureAwait(continueOnCapturedContext);
            }
            catch (System.Exception ex) when (onException != null)
            {
                onException?.Invoke(ex);
            }
        }
    }

    /// 
    /// Weak event manager that allows for garbage collection when the EventHandler is still subscribed
    /// 
    public class WeakEventManager
    {
        readonly Dictionary> _eventHandlers = new Dictionary>();

        /// 
        /// Adds the event handler
        /// 
        /// Handler
        /// Event name
        public void AddEventHandler(Delegate handler, [CallerMemberName] string eventName = "")
    {
            if (IsNullOrWhiteSpace(eventName))
                throw new ArgumentNullException(nameof(eventName));

            if (handler is null)
                throw new ArgumentNullException(nameof(handler));

            EventManagerService.AddEventHandler(eventName, handler.Target, handler.GetMethodInfo(), _eventHandlers);
        }

        /// 
        /// Removes the event handler.
        /// 
        /// Handler
        /// Event name
        public void RemoveEventHandler(Delegate handler, [CallerMemberName] string eventName = "")
        {
            if (IsNullOrWhiteSpace(eventName))
                throw new ArgumentNullException(nameof(eventName));

            if (handler is null)
                throw new ArgumentNullException(nameof(handler));

            EventManagerService.RemoveEventHandler(eventName, handler.Target, handler.GetMethodInfo(), _eventHandlers);
        }

        /// 
        /// Executes the event
        /// 
        /// Sender
        /// Event arguments
        /// Event name
        public void HandleEvent(object sender, object eventArgs, string eventName) => EventManagerService.HandleEvent(eventName, sender, eventArgs, _eventHandlers);
    }

    /// 
    /// An Async implmentation of ICommand
    /// 
    public interface IAsyncCommand : System.Windows.Input.ICommand
    {
        /// 
        /// Executes the Command as a Task
        /// 
        /// The executed Task
        /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
        System.Threading.Tasks.Task ExecuteAsync(T parameter);
    }

    /// 
    /// An Async implmentation of ICommand
    /// 
    public interface IAsyncCommand : System.Windows.Input.ICommand
    {
        /// 
        /// Executes the Command as a Task
        /// 
        /// The executed Task
        System.Threading.Tasks.Task ExecuteAsync();
    }
}

7
задан Roman Shumikhin 30 December 2008 в 14:34
поделиться

4 ответа

Необходимо было бы использовать Read(char[], int, int) перегрузка (который действительно ограничивает длину) и делает Ваше собственное обнаружение конца строки; не должно быть слишком хитрым.

Для немного ленивой версии (который использует единственную-characted версию чтения):

static IEnumerable<string> ReadLines(string path, int maxLineLength)
{
    StringBuilder currentLine = new StringBuilder(maxLineLength);
    using (var reader = File.OpenText(path))
    {
        int i;
        while((i = reader.Read()) > 0) {
            char c = (char) i;
            if(c == '\r' || c == '\n') {
                yield return currentLine.ToString();
                currentLine.Length = 0;
                continue;
            }
            currentLine.Append((char)c);
            if (currentLine.Length > maxLineLength)
            {
                throw new InvalidOperationException("Max length exceeded");
            }
        }
        if (currentLine.Length > 0)
        {
            yield return currentLine.ToString();
        }                
    }
}
10
ответ дан 6 December 2019 в 11:53
поделиться

Вам, возможно, понадобился бы один из StreamReader.Read перегрузка:

Взятый из http://msdn.microsoft.com/en-us/library/9kstw824.aspx

    using (StreamReader sr = new StreamReader(path)) 
    {
        //This is an arbitrary size for this example.
        char[] c = null;

        while (sr.Peek() >= 0) 
        {
            c = new char[5];
            sr.Read(c, 0, c.Length);
            //The output will look odd, because
            //only five characters are read at a time.
            Console.WriteLine(c);
        }
    }

Внимание на sr.Read(c, 0, c.Length) строка. Это читает только 5 символов из потока и вставленный в c массив. Можно хотеть измениться 5 для оценки Вас, хотят.

4
ответ дан 6 December 2019 в 11:53
поделиться

Вот мое собственное решение на основе решения Marc Gravell:

using System;
using System.IO;
using System.Text;

namespace MyProject
{
    class StreamReaderExt : StreamReader
    {

        public StreamReaderExt(Stream s, Encoding e) : base(s, e)
        {            
        }

        /// <summary>
        /// Reads a line of characters terminated by CR+LF from the current stream and returns the data as a string
        /// </summary>
        /// <param name="maxLineLength">Maximum allowed line length</param>
        /// <exception cref="System.IO.IOException" />
        /// <exception cref="System.InvalidOperationException">When string read by this method exceeds the maximum allowed line length</exception>
        /// <returns></returns>
        public string ReadLineCRLF(int maxLineLength)
        {
            StringBuilder currentLine = new StringBuilder(maxLineLength);

            int i;
            bool foundCR = false;
            bool readData = false;

            while ((i = Read()) > 0)
            {

                readData = true;

                char c = (char)i;

                if (foundCR)
                {
                    if (c == '\r')
                    {
                        // If CR was found before , and the next character is also CR,
                        // adding previously skipped CR to the result string
                        currentLine.Append('\r');
                        continue;
                    }
                    else if (c == '\n')
                    {
                        // LF found, finished reading the string
                        return currentLine.ToString();
                    }
                    else
                    {
                        // If CR was found before , but the next character is not LF,
                        // adding previously skipped CR to the result string
                        currentLine.Append('\r');
                        foundCR = false;
                    }
                }
                else // CR not found
                {
                    if (c == '\r')
                    {
                        foundCR = true;
                        continue;
                    }
                }

                currentLine.Append((char)c);
                if (currentLine.Length > maxLineLength)
                {
                    throw new InvalidOperationException("Max line length exceeded");
                }
            }

            if (foundCR)
            {
                // If CR was found before, and the end of the stream has been reached, appending the skipped CR character
                currentLine.Append('\r');
            }

            if (readData)
            {
                return currentLine.ToString();
            }

            // End of the stream reached
            return null;

        }
    }
}

Эта часть кода обеспечивается "AS," без ГАРАНТИИ.

2
ответ дан 6 December 2019 в 11:53
поделиться

Можно всегда использовать ".Read (...)", и MSDN рекомендует делать так для ситуации как Ваша.

http://msdn.microsoft.com/en-us/library/system.io.streamreader.readline.aspx

1
ответ дан 6 December 2019 в 11:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: