#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. @trican: Каждый раз, когда
lno
достигается степень 2 (например), затем объединяйтеfdata
с массивом пустых матриц размером в 1lno
ячейку, т.е.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;
).