Чтение и обработка большого текстового файла в Matlab

#file #matlab #io

#файл #matlab #io

Вопрос:

Я пытаюсь прочитать большой текстовый файл (несколько миллионов строк) в Matlab. Изначально я использовал importdata(имя_файла), что казалось кратким решением. Однако мне нужно использовать Matlab 7 (да, я знаю его старый), и, похоже, importdata не поддерживается. Таким образом, я попробовал следующее:

 while ~feof(fid)    
    fline = fgetl(fid);
    fdata{1,lno} =  fline ;
    lno = lno   1;
end
  

Но это действительно медленно. Я предполагаю, что это связано с изменением размера массива на каждой итерации. Есть ли лучший способ сделать это. Принимая во внимание, что первые 20 строк входных данных являются данными строкового типа, а остальные данные представляют собой от 3 до 6 столбцов шестнадцатеричных значений.

Ответ №1:

вам придется немного изменить форму, но другим вариантом для вас будет использование fread. Но, как уже упоминалось, это по существу блокирует вас в прямоугольном импорте. Таким образом, другим вариантом было бы использовать textscan. Как я упоминал в другой заметке, я не уверен на 100%, когда это было реализовано, все, что я знаю, это то, что у вас нет «importdata ()»

 fid = fopen('textfile.txt')
Out  = textscan(fid,'%s','delimiter',sprintf('n'));
fclose(fid)
  

с помощью textscan вы сможете получить массив ячеек с символами для каждой строки, которыми затем можете манипулировать по своему усмотрению. И, как я говорю в своих комментариях, это больше не имеет значения, одинаковой длины строки или нет. ТЕПЕРЬ вы можете быстрее анализировать массив ячеек. Но, как упоминает gnovice, и у него также есть очень элегантное решение, вам, возможно, придется беспокоиться о требованиях к памяти.

Единственное, что вы никогда не захотите использовать в matlab, если можете этого избежать, — это циклические структуры. Они быстры в C / C и т.д., Но в matlab это самый медленный способ добраться туда, куда вы направляетесь.

РЕДАКТИРОВАТЬ: Только что просмотрел, и похоже, что textscan БЫЛ реализован буквально в версии 7 (R14), так что, если это то, что у вас есть, вы должны быть готовы использовать это.

Комментарии:

1. О, теперь вы просто проверяете мои навыки здесь 🙂 Я отредактировал свой пост, чтобы отразить способ, которым вы можете обойтись без того, чтобы строки были одинаковой длины.

2. Хороший улов в TEXTSCAN. Я не был уверен, доступно ли это еще в версии 7. Мое решение на основе FSCANF делает в основном то же самое, но с большим количеством ввода :).

3. Да, вот почему я поддержал ваш. Это очень хорошее решение, которое использует множество обходных путей для потенциально отсутствующих функций. Когда в Matlab нет функции для этого, цикл for или while почти никогда не является подходящим местом для начала! Я бы предпочел написать свой собственный MEX, чем каждый раз проходить через несколько миллиардов итераций цикла for 🙂

Ответ №2:

Я вижу два варианта:

  1. Вместо того, чтобы каждый раз увеличиваться на 1, вы могли бы, например, удваивать размер вашего массива только при необходимости. Это значительно уменьшает количество требуемых перераспределений.
  2. Выполните двухпроходный подход. Первый проход просто подсчитывает количество строк, не сохраняя их. Второй проход фактически заполняет массив (который был предварительно выделен до правильного размера).

Комментарии:

1. Кажется разумным — как бы я это сделал. Поскольку я немного смущен использованием ячеек {} по сравнению с обычными массивами.

2. @trican: Каждый раз, когда lno достигается степень 2 (например), затем объединяйте fdata с массивом пустых матриц размером в 1 lno ячейку, т.е. fdata = [fdata cell(1,lno)] .

3. Вы также можете расширить его, присвоив элементу за концом: fdata(end*2) = fdata(1); . Работает с большинством типов данных.

Ответ №3:

Одним из решений является чтение всего содержимого файла в виде строки символов с помощью FSCANF, разделение строки на отдельные ячейки в местах, где встречаются символы новой строки, с помощью MAT2CELL, удаление лишних пробелов на концах с помощью STRTRIM, затем обработка строковых данных в каждой ячейке по мере необходимости. Например, используя этот пример текстового файла 'junk.txt' :

 hi
hello
1 2 3
FF 00 FF
12 A6 22 20 20 20
FF FF FF
  

Следующий код поместит каждую строку в ячейку массива cell cellData :

 >> fid = fopen('junk.txt','r');
>> strData = fscanf(fid,'%c');
>> fclose(fid);
>> nCharPerLine = diff([0 find(strData == char(10)) numel(strData)]);
>> cellData = strtrim(mat2cell(strData,1,nCharPerLine))

cellData = 

    'hi'    'hello'    '1 2 3'    'FF 00 FF'    '12 A6 22 20 20 20'    'FF FF FF'
  

Теперь, если вы хотите преобразовать все шестнадцатеричные данные (строки с 3 по 6 в моем примере файла данных) из строк в векторы чисел, вы можете использовать CELLFUN и SSCANF следующим образом:

 >> cellData(3:end) = cellfun(@(s) {sscanf(s,'%x',[1 inf])},cellData(3:end));
>> cellData{3:end}    %# Display contents

ans =

     1     2     3

ans =

   255     0   255

ans =

    18   166    34    32    32    32

ans =

   255   255   255
  

ПРИМЕЧАНИЕ: Поскольку вы имеете дело с такими большими массивами, вам придется помнить о объеме памяти, используемом вашими переменными. Приведенное выше решение векторизовано, но может занимать много памяти. Возможно, вам придется перезаписать или очистить большие переменные, как strData при создании cellData . В качестве альтернативы, вы могли бы перебирать элементы в nCharPerLine и индивидуально обрабатывать каждый сегмент строки большего размера strData в нужные вам векторы, которые вы можете предварительно выделить, теперь, когда вы знаете, сколько строк данных у вас есть (т.Е. nDataLines = numel(nCharPerLine)-nHeaderLines; ).