Eng-Tips is the largest engineering community on the Internet

Intelligent Work Forums for Engineering Professionals

How to parse strings character by character?

Status
Not open for further replies.

blhs

Mechanical
Mar 16, 2002
3
0
0
US
One of the more frustrating things when coming from Nice Old FORTRAN(NOF) is input/output. I have text output files from programs like NASTRAN which can vary slightly from version to version. My simple FORTRAN code can parse the text line by line and character by character until I match a key word, then the real program can take over. Another program's text output may have a text header but contain a floating point I want to capture, then have inconsistant row data (inconstant due to poor output routines of the originating code such that one line has 5 floats, the next five more, then two, then five, etc.). I am forcing myself to use Matlab as a general rule these days, but fundamental I/O like this is not obvious (to me anyway).

Has anyone conquered this (and how)???
 
Replies continue below

Recommended for you

Below is one approach to reading FORTRAN (formatted) files. Basically, the file is read with a textread command, and converted into a character array. Key header lines (that indicate the start of data) are located with the strmatch command. The data can be check to see if stars were written out (fortran overflow) with the findstr command. And the numbers are read with the str2num command. If you need/want further guidance on your specific example, let me know. The example below is somewhat complex, since the data may have overflow (stars) and the number of eigenvalues to be read changes for each case. But you should be able to follow it.

In step by step directions

1) Open the file with textread, and convert to a character array (using char command). Store in a character array named file.

example code: Open a file named 'fortran.dat'

fname = 'fortran.dat'
file = char(textread(deblank(fname),
'%s','delimiter','\n','whitespace',''));

2) Find key lines using strmatch command. A key line is a line with some type of a header, and the data that you want to read is a fixed number of lines from that header.

example code: Find all occurances of the header line "1EIGENVALUES"

eigen_row=strmatch('1EIGENVALUES',file);

3) Determine how many header lines were found via size command.

example code: Determine the number of 1EIGENVALUE headers

ni = size(eigen_row,1);

4) Loop through the header lines with for statement

example code: Loop through header lines

for ii=1:ni

5) Numbers can be read with the str2num command. You need to specifiy how many rows down from the header line, and the column range.

typical code: num = str2num(file(header_row+nlines,start_col:end_col));

example code:

% Start and end of eigenvalues

ic_start = eigen_row(ii)+12;
ic_end = ic_start+mxx(ii)-1;
ic_err = eigen_row(ii)-3;

% Copy data into temp array

eig_lines=file(ic_start:ic_end,:);

% Correct each line for length

eig_lines = my_adjust_eig(eig_lines);

% Read each of the eigenvalue table lines

nlines = size(eig_lines,1);
neig=0;

for iline=1:nlines % Loop over the lines

line=eig_lines(iline,:);

[im_lambda, err] = check_str(line(47:57));

if (im_lambda >= 0)
neig = neig+1;
freq_hz(neig,ii) = check_str(line(71:83));
end

end


6) If needed, use findstr to check for valid data. For fortran, you could check for stars (overflow write)

stars = findstr(str,'*');
if isempty(stars) ~= 0

num = str2num(str);
err = 0;
if length(num)~=1
num = nan;
err=1;
end
else
num = nan;
err=1;
end


Here is a complete program, you should be able to follow it

function read_freq

fname='freq9b.com.out';
[rpm,coll,f_cpm]=readcmrd(fname);

iline=0;
for icase=1:size(f_cpm,2);
% for ifreq=size(f_cpm,1):-1:1
for ifreq=size(f_cpm,1):-1:size(f_cpm,1)
iline=iline+1;
% s=sprintf('%6.4f %4.1f %4.0f', f_cpm(ifreq,icase)/rpm(icase), rpm(icase), coll(icase));
s=sprintf('%5.1f %5.1f %6.4f', coll(icase), rpm(icase), f_cpm(ifreq,icase)/rpm(icase));
ns=size(s,2);
sp(iline,1:ns)=s;
end
iline=iline+1;
end
sp

function [rotor_speed_rpm,coll_deg,freq_cpm] = readcmrd(fname)

file = char(textread(deblank(fname),'%s','delimiter','\n','whitespace',''));
eigen_row=strmatch('1EIGENVALUES',file);
performance_row=strmatch('ROTORCRAFT PERFORMANCE ',file);
mxx_row = strmatch('NUMBER OF STATES, MXX =',file);
mxx=str2num(file(mxx_row,27:30));
ni = size(eigen_row,1);
for ii=1:ni
% Read the rotor speed
rotor_speed_rpm(ii) = str2num(file(performance_row(ii)+11,112:120));
coll_deg(ii) = str2num(file(performance_row(ii)+19,25:35));
% Read the eigenvalues
% Two difficulties in reading eigenvalues
% 1) The initial spaces are skipped, so the location of the number
% depends on how much white space is on that line
% 2) Data with stars in freq, damp, real/sec or imag/sec has to be removed
% 3) Remove data with negative imaginary part
% 4) Print information if an error occurs

% Start and end of eigenvalues

ic_start = eigen_row(ii)+12;
ic_end = ic_start+mxx(ii)-1;
ic_err = eigen_row(ii)-3;

if (strcmp(file(ic_err,1:9),'*** ERROR') == 0)

% Copy data into temp array

eig_lines=file(ic_start:ic_end,:);

% Correct each line for length

eig_lines = my_adjust_eig(eig_lines);

% Read each of the eigenvalue table lines

nlines = size(eig_lines,1);
neig=0;

for iline=1:nlines % Loop over the lines

line=eig_lines(iline,:);

[im_lambda, err] = check_str(line(47:57));

if (im_lambda >= 0)
neig = neig+1;
freq_hz(neig,ii) = check_str(line(71:83));
end

end

else

end

end

freq_cpm = freq_hz*60;

function lines2 = my_adjust_eig(lines1)

% The last character in each line is in column 127

nlines = size(lines1,1);

for iline=1:nlines
sp = findstr(lines1(iline,:),' ');
lastchr = size(lines1,2);
csp = size(sp,2);
while (lastchr == sp(csp))
lastchr = lastchr-1;
csp=csp-1;
end
fchar=121;
if((lines1(iline,lastchr)== 'n') == 1)
lines2(iline,1:fchar-lastchr-12) = ' ';
lines2(iline,fchar-lastchr-12+1:fchar-12) = lines1(iline,1:lastchr);
lines2(iline,fchar-12+1:fchar) = ' ';
else
lines2(iline,1:fchar-lastchr) = ' ';
lines2(iline,fchar-lastchr+1:fchar) = lines1(iline,1:lastchr);
end

end

function [num, err] = check_str(str);

stars = findstr(str,'*');
if isempty(stars) ~= 0

num = str2num(str);
err = 0;
if length(num)~=1
num = nan;
err=1;
end
else
num = nan;
err=1;
end

 
Great! Thank you, I took your examples and was able to develop code to do what I needed. This will be very helpful in the future. Thank you again.
 
Status
Not open for further replies.
Back
Top