ReadLine() против Read() для эффективного получения CR и LF?

Я работаю над программой C#, чтобы определить длину строки для каждой строки в нескольких больших текстовых файлах с более чем 100 000 строк перед импортом с использованием пакета SSIS. Я также буду проверять другие значения в каждой строке, чтобы убедиться, что они верны, прежде чем импортировать их в мою базу данных с помощью SSIS.

Например, я ожидаю длину строки в 3000 символов, затем CR в 3001 и LF в 3002, так что всего 3002 символа.

При использовании ReadLine() он считывает CR или LF как конец строки, поэтому я не могу проверить символы CR или LF. Я просто проверял длину линии на 3000, чтобы определить, была ли она правильной. Я только что столкнулся с проблемой, когда файл имеет LF в позиции 3001, но отсутствует CR. Итак, ReadLine() говорит, что это 3000 символов, ведь это правильно, но в моем пакете SSIS произойдет сбой, потому что в нем отсутствует CR.

Я проверил, что Read() будет достигать каждого символа 1 за раз, и я могу определить, имеет ли каждая строка CR и LF, но это кажется довольно непродуктивным, и когда некоторые файлы, с которыми я столкнусь, имеют более 5 000 000 строк, это кажется очень неэффективный. Мне также нужно будет затем добавить каждый символ в строку или использовать ReadBlock() и преобразовать массив символов в строку, чтобы я мог проверить другие значения в строке.

Есть ли у кого-нибудь какие-либо идеи относительно эффективного способа проверки строки на наличие CR и LF и других значений в данной строке, не тратя ненужных ресурсов и заканчивая относительно своевременно.


person buzzzzjay    schedule 01.09.2011    source источник
comment
*Примечание: я пытался использовать Peek() с ReadLine(), и он начинает читать следующую строку вместо чтения CR и LF. Я надеялся, что это будет простое решение. Похоже, что после использования ReadLine() он удаляет CR и LF из StreamReader.   -  person buzzzzjay    schedule 02.09.2011
comment
Для уточнения: несмотря на то, что вы проверяете действительные данные, выполняется ли импорт исходного необработанного файла или данных, которые вы уже загрузили в свою программу C#? Я предполагаю первое, но хотел быть уверенным.   -  person JaredReisinger    schedule 02.09.2011
comment
Это кажется неэффективным? Чтение файлов ограничивается скоростью жесткого диска или сети. Вы можете использовать StreamReader.Read(char[], int, int) для чтения набора символов.   -  person Hans Passant    schedule 02.09.2011
comment
@JaredReisinger Я импортирую файл необработанных данных. Я пытаюсь выполнить предварительную проверку данных, чтобы предотвратить сбой процесса импорта, потому что мне отправлен файл с неверными данными.   -  person buzzzzjay    schedule 02.09.2011


Ответы (5)


подтвердили, что Read() будет достигать каждого символа по 1 за раз, и я могу определить, есть ли в каждой строке CR и LF, но это кажется довольно непродуктивным

Думать об этом. Как вы думаете, у ReadLine() есть волшебная палочка и ему не нужно читать каждый символ?

Просто создайте свой собственный ReadMyLine(). Что-то должно читать символы, неважно, ваш код или библиотека. Ввод-вывод будет буферизоваться Stream и Windows.

person Henk Holterman    schedule 01.09.2011
comment
Я не против создания собственного кода, но эффективность очень важна при использовании этого для проверки 100 000 строк. Я просто не могу поверить, что это не то, с чем другие еще не сталкивались, или это не функция по умолчанию. Есть ли у вас какие-либо предложения о том, как создать ReadMyLine(). - person buzzzzjay; 02.09.2011
comment
В основном я создал ReadMyLine(). См. выше. Спасибо! - person buzzzzjay; 02.09.2011

Можно ли использовать переопределение StreamReader.Read ИЛИ переопределение TextReader.Read, которое принимает 3 параметра - строковый буфер (в вашем случае 3002 символа массив), индекс startint (вы будете обрабатывать это в цикле, каждый раз увеличивая индекс на 3002), количество символов для чтения (3002). Из буфера чтения вы можете проверить последние два символа для условной оценки CR и LF.

person Arun    schedule 01.09.2011
comment
Я мог бы, и я в настоящее время пробую это как возможность. Однако это крайне неэффективно в тестах, которые я уже пытался использовать с файлами, содержащими 100 000 записей. - person buzzzzjay; 02.09.2011
comment
Альтернативой может быть использование ДВУХ StreamReaders - один, где вы будете использовать ReadLine для чтения строки, а другой - для чтения только последних двух символов в буфер char[2]. Каждый раз, когда последние два не имеют CR + LF, вы знаете, что в линии есть проблема. Таким образом, вы не будете многократно использовать массив из 3002 символов в цикле. - person Arun; 02.09.2011

Я считаю, что вы найдете эту версию эффективной:

    static bool CheckFile(string filename)
    {
        const int BUFFER_SIZE = 3002;

        var Reader = new StreamReader(filename, Encoding.ASCII, false, BUFFER_SIZE);

        var buffer = new char[BUFFER_SIZE];

        int offset = 0;
        int bytesRead = 0;

        while((bytesRead = Reader.Read(buffer, offset, BUFFER_SIZE)) > 0)
        {
            if(bytesRead != BUFFER_SIZE 
                || buffer[BUFFER_SIZE - 2] != '\r' 
                || buffer[BUFFER_SIZE - 1] != '\n')
            {
                //the file does not conform
                return false;
            }

            offset += bytesRead;
        }

        return true;
    }

Причина моего оптимизма в том, что, согласно документам, эффективность увеличивается, если размер базового буфера соответствует размеру буфера, используемого для чтения. Предупреждение: этот код не тестировался и не измерялся по времени.

person Paul Keister    schedule 01.09.2011

Я могу что-то здесь упустить, но если данные в каждой строке всегда ровно 3000 символов (исключая CR и LF)?

Почему бы просто не прочитать каждую строку, а затем взять только первые 3000 символов, используя string.Substring(). Таким образом, вам не нужно беспокоиться о том, как именно завершается строка.

ie

 using (StreamReader sr = new StreamReader("TestFile.txt")) 
    {
       String line;
       while ((line = sr.ReadLine()) != null) 
          {
            // string data = line.subString(0,3000); 
            // edit, if data is sometimes < 3000 ....  
            string data = line.subString(0,line.length < 3000 ? line.length : 3000);
            // do something with data
          }
     }
person inspite    schedule 01.09.2011
comment
Раньше я использовал подобный метод. Строка всегда должна состоять из 3000 символов, исключая CR и LF. Однако это не всегда так, и поэтому мне нужно проверить длину, потому что я получаю файлы из множества разных источников, которые не всегда имеют правильную длину. Если длина меньше 3000 символов, и вы подстроите ее, это приведет к сбою, выбросу и исключению. - person buzzzzjay; 02.09.2011
comment
@buzzz Не думайте, что что-то работает медленно, измерьте. Ваша основная стоимость будет заключаться в вводе-выводе, а не в обработке символов/строк. Я бы использовал while(...) { int ch = s.Read(); ... } - person Henk Holterman; 02.09.2011
comment
ну ты можешь взять line.length < 3000 ? line.length : 3000 - person inspite; 02.09.2011

Я думаю, что наконец понял код, чтобы получить именно то, что я хочу, мысли? Основная проблема, с которой я столкнулся, заключалась в том, что мне не гарантируется, что длина моей строки будет правильной. В противном случае метод, упомянутый @Paul Keister, отлично сработал бы, и я его тестировал. Спасибо за помощь!

int asciiValue = 0;

while (asciiValue != -1)
{

Boolean endOfRow = false;
Boolean endOfRowValid = true;

string currentLine = "";

while (endOfRow == false)
{
    asciiValue = file.Read();

    if (asciiValue == 10 || asciiValue == 13)
    {
        int asciiValueTemp = file.Peek();

        if (asciiValue == 13 && asciiValueTemp == 10)
        {
            endOfRow = true;
            asciiValue = file.Read();
        }
        else
        {
            endOfRowValid = false;
            endOfRow = true;
        }
    }
    else if (asciiValue != -1)
        currentLine += char.ConvertFromUtf32(asciiValue);
    else
        endOfRow = true;
}

Изменить: я забыл упомянуть, что это кажется таким же эффективным, как использование ReadLine(). Я очень боялся, что это тоже не сработает. Похоже, я был неправ.

person buzzzzjay    schedule 02.09.2011