Using Matlab to make modification to a text file - matlab

Essentially I am writing a Matlab file to change the 2nd, 3rd and 4th numbers in the line below "STR" and above "CON" in the text file (which is given below and is called '0.dat'). Currently, my Matlab code makes no changes to the text file.
Text File
pri
3
len 0.03
vic 5 5
MAT
1 147E9 0.3 0 4.9E9 8.5E9
LAY
1 0.000125 1 45
2 0.000125 1 0
3 0.000125 1 -45
4 0.000125 1 90
5 0.000125 1 45
WAL
1 1 2 3 4 5
PLATE
1 0.005 1 1
STR
1 32217.442335442 3010.34241024889 2689.48842888812
CON
1 2 1 2 3 1 3 4 1 4 5 1 5 6 1 6 7 1
ATT
1 901 7 901
LON
34
POI
123456
1 7
X 0.015
123456
2 6
X 0.00381966011250105 0.026180339887499
123456
3 5
X 0.000857864376269049 0.0291421356237309
123456
4
X 0
PLO
2 3
CRO
0
RES
INMOD=1
END
Matlab code:
impafp = importdata('0.dat','\t');
afp = impafp.textdata;
fileID = fopen('0.dat','r+');
for i = 1:length(afp)
if (strncmpi(afp{i},'con',3))
newNx = 100;
newNxy = 50;
newNy = 500;
myformat = '%0.6f %0.9f %0.9f %0.9f\n';
newData = [1 newNx newNxy newNy];
afp{i-1} = fprintf(fileID, myformat, newData);
fclose(fileID);
end
end

From the help for importdata:
For ASCII files and spreadsheets, importdata expects to find numeric
data in a rectangular form (that is, like a matrix). Text headers can
appear above or to the left of numeric data. To import ASCII files
with numeric characters anywhere else, including columns of character
data or formatted dates or times, use TEXTSCAN instead of import data.
Indeed, if you print out the value of afp, you'll see that it just contains the first line. You were also not performing any operation that was writing to a file. And you were not closing the file ID if the if state wasn't triggered.
Here is one way to do this with textscan (which is probably faster too):
% Read in data as strings using textscan
fid = fopen('0.dat','r');
afp = textscan(fid,'%s','Delimiter','');
fclose(fid);
isSTR = strncmpi(afp{:},'str',3); % True for all lines starting with STR
isCON = strncmpi(afp{:},'con',3); % True for all lines starting with CON
% Find indices to replace - create logical indices
% True if line before is STR and line after is CON
% Offset isSTR and isCON by 2 elements in opposite directions to align
% Use & to perform vectorized AND
% Pad with FALSE on either side to make output the same length as afp{1}{:}
datIDX = [false;(isSTR(1:end-2)&isCON(3:end));false];
% Overwrite data using sprintf
myformat = '%0.6f %0.9f %0.9f %0.9f';
newNx = 100;
newNxy = 50;
newNy = 500;
newData = [1 newNx newNxy newNy];
afp{1}{datIDX} = sprintf(myformat, newData); % Set only elements that pass test
% Overwrite old file using fprintf (or change filename to new one)
fid = fopen('0.dat','w');
fprintf(fid,'%s\r\n',afp{1}{1:end-1});
fprintf(fid,'%s',afp{1}{end}); % Avoid blank line at end
fclose(fid);
If you're unfamiliar with logical indexing, you might read this blog post and this.

I would recommend just reading the entire file in, finding which lines contain your "keywords", modifying specific lines, and then writing it back out to a file, which can have the same name or a different one.
file = fileread('file.dat');
parts = regexp(file,'\n','split');
startIndex = find(~cellfun('isempty',regexp(parts,'STR')));
endIndex = find(~cellfun('isempty',regexp(parts,'CON')));
ind2Change = startIndex+1:endIndex-1;
tempCell{1} = sprintf('%0.6f %0.9f %0.9f %0.9f',[1,100,50,500]);
parts(ind2Change) = deal(tempCell);
out = sprintf('%s\n',parts{:});
out = out(1:end-1);
fh = fopen('file2.dat','w');
fwrite(fh,out);
fclose(fh);

Related

Matlab from text file to sparse matrix.

I have a huge text file in the following format:
1 2
1 3
1 10
1 11
1 20
1 376
1 665255
2 4
2 126
2 134
2 242
2 247
First column is the x coordinate while second column is the y coordinate.
It indicates that if I had to construct a Matrix
M = zeros(N, N);
M(1, 2) = 1;
M(1, 3) = 1;
.
.
M(2, 247) = 1;
This text file is huge and can't be brought to main memory at once. I must read it line by line. And save it in a sparse matrix.
So I need the following function:
function mat = generate( path )
fid = fopen(path);
tline = fgetl(fid);
% initialize an empty sparse matrix. (I know I assigned Mat(1, 1) = 1)
mat = sparse(1);
while ischar(tline)
tline = fgetl(fid);
if ischar(tline)
C = strsplit(tline);
end
mat(C{1}, C{2}) = 1;
end
fclose(fid);
end
But unfortunately besides the first row it just puts trash in my sparse mat.
Demo:
1 7
1 9
2 4
2 9
If I print the sparse mat I get:
(1,1) 1
(50,52) 1
(49,57) 1
(50,57) 1
Any suggestions ?
Fixing what you have...
Your problem is that C is a cell array of characters, not numbers. You need to convert the strings you read from the file into integer values. Instead of strsplit you can use functions like str2num and str2double. Since tline is a space-delimited character array of integers in this case, str2num is the easiest to use to compute C:
C = str2num(tline);
Then you just index C like an array instead of a cell array:
mat(C(1), C(2)) = 1;
Extra tidbit: If you were wondering how your demo code still worked even though C contained characters, it's because MATLAB has a tendency to automatically convert variables to the correct type for certain operations. In this case, the characters were converted to their double ASCII code equivalents: '1' became 49, '2' became 50, etc. Then it used these as indices into mat.
A simpler alternative...
You don't even have to bother with all that mess above, since you can replace your entire function with a much simpler approach using dlmread and sparse like so:
data = dlmread(filePath);
mat = sparse(data(:, 1), data(:, 2), 1);
clear data; % Save yourself some memory if you don't need it any more

MATLAB: How to read data from a text file that have a certain character

I have a txt file, which contains a bunch of random data in two units, Distance (m) and Time (seconds). For example the txt file looks like this:
5 metre
0 sec
10 metre
1 sec
16 metre
2 sec
25 metre
3 sec
I want to be able to store all of the metres values into an array, and the seconds data into another. Basically separating the text file into two arrays based on the metre/sec units.
You could use readtable
I don't know whether this is the 'right' function, but this works :)
Read Data:
A = readtable('Input.txt','Delimiter',' ','ReadVariableNames',false);
Output would be something like this:
A =
Var1 Var2
____ _______
5 'metre'
0 'sec'
10 'metre'
1 'sec'
16 'metre'
2 'sec'
25 'metre'
3 'sec'
%// use 'cellfun' to create a mask of which unit corresponds to 'metre'
mask = cellfun(#(x) strcmp(x,'metre'), A.Var2);
%// save the corresponding data in one variable and rest in another
m = A.Var1(mask);
s = A.Var1(~mask);
Output:
>> m
m =
5
10
16
25
>> s
s =
0
1
2
3
Try this:
% Build a dummy txt data
txt = '';
for i=1:100
txt = sprintf('%s%d metre\n%d sec\n',txt,2*i, i);
end
txt = regexprep(txt,'\n\n','\n')
data = sscanf(txt,'%d metre\n%d sec\n',[2 Inf])';
the regexprep() should take care of accidental double line breaks, while sscanf() parses the data in the right format.

Loading huge data efficiently in matlab

I have a million rows data set in the following format
User Gameid Count
A 1 2
A 2 3
A 10 2
A 8 2
B 10 2
B 1 6
....
I want to create a sparse matrix from this with each row representing a user and the columns representing the gameid and the value in each cell is the count of the corresponding user and gameid.
The matrix is sparse. So it should be handled by matlab.
How can I directly load such data without iterating through each line which will take a lot of time. Any suggestions how to do it efficiently?
This is what I want
Column 1 2 3 4 5 6 7 8 9 10
2 3 2 2
6 2
I think you can use textscan.
fid = fopen(file);
content = textscan(fid, '%s%d%d');
user = content{1}; % cell
field = content{2}; % vector
count = content{3}; % vector
ok your file is file.txt
Ok so with an easy manipulation you can get what u want:
clear all
close all
fil = 'file.txt';
fid = fopen(fil,'r');
s = textscan(fid,'%s','Delimiter','\n');
s = s{1};
s = s(2:end);
ls = length(s);
fmat = '%s %u %u';
C = sscanf(char(s)',fmat);
C = reshape(C,[3 length(s)])';
A = char(C(:,1));
data = cellstr(A);
data(:,2:3) = num2cell(C(:,2:3));
Users = unique(A);
S_MAT_DATA = spalloc(length(Users),max(C(:,2)),round(ls/length(Users))+1);
nmat = 1;
for aa = 1:length(Users)
data_user = cell2mat(data(strncmp(Users(aa),data(:,1),1),2:3)');
S_MAT_DATA(aa,data_user(1,:)) = data_user(2,:);
end

how to read a matrix from a text file in matlab

I have a text file which has 500 columns and 500 rows, of numerical(integer) values . Every element in the row is separated by a tab. I want to read this file as a matrix in matlab. Example(my text file is like this):
1 2 2 1 1 2
0 0 0 1 2 0
1 2 2 1 1 2
0 0 0 1 2 0
And after reading this text file as a matrix (a[]) in matlab I want to do transpose.
Help me.
You can use importdata.
Something like:
filename = 'myfile01.txt';
delimiterIn = '\t';
headerlinesIn = 1;
A = importdata(filename,delimiterIn,headerlinesIn);
A_trans = A';
You can skip headerlines if your file does not have any haeder.. (It is the number of lines before the actual data starts)
Taken from Matlab documentation, improtdata
Have you tired load with -ascii option?
For example
a = load('myfile.txt', '-ascii'); % read the data
a = a.'; %' transpose
% Pre-allocate matrix
Nrow=500; Ncol=500;
a = zeros(Nrow,Ncol);
% Read file
fid = fopen('yourfile.txt','r');
for i:1:Nrow
a(i,:) = cell2mat(textscan(fid,repmat('%d ',Ncol));
end
fclose(fid);
% Trasnspose matrix
a_trans = a.';
You could simply do:
yourVariable = importdata('yourFile.txt')';
%Loads data from file, transposes it and stores it into 'yourVariable'.

How can I merge this data in MATLAB?

In the sample text file below, if column 3 contains a 1 then the corresponding data of column 2 should be merged with the data of the previous row in column 2. For example, the 40 in row 2 should be added to the 10 in row 1, then row 2 should be set to 0 (as shown in the modified sample text file). The problem with my code below is that it only records changes in the current data time(i,1) but not the changes made for the previous data.
original.txt
a time c
1 10 0
2 40 1
3 20 0
4 11 0
5 40 1
modified.txt
a time c
1 50 0
2 0 0
3 20 0
4 51 0
5 0 0
fid=fopen('data.txt');
A=textscan(fid,'%f%f%f');
a =A{1};
time=A{2};
c =A{3};
fclose(fid);
fid=fopen('newData.txt','wt');
for i=1:size(a)
if c(i,1)==1
time(i-1,1)=time(i,1)+time(i-1,1); % merge the time of the current and the previous
time(i,1) =0; %set the time to 0
array = []; %empty the array
array = [a(i,1) time c(i,1)]; add new data
format short g;
fprintf(fid,'%g\t %g\t %g\n',array);
end
fclose(fid)
The reason the current time value is written properly but the previous one isn't is because you have already written the previous one to the file on the previous loop iteration, so there is no way for you to change it. You need to remove the printing from within the loop and add it after you adjust all of the time values.
You can also take advantage of vectorization by using the FIND function instead of a for loop. You also only need one call to FPRINTF to output all the data. Try this:
a = [1; 2; 3; 4; 5]; %# Sample data
time = [10; 40; 20; 11; 40]; %# Sample data
c = [0; 1; 0; 0; 1]; %# Sample data
index = find(c == 1); %# Find indices where c equals 1
temp = time(index); %# Temporarily store the time values
time(index) = 0; %# Zero-out the time points
time(index-1) = time(index-1)+temp; %# Add to the previous time points
c(index) = 0; %# Zero-out the entries of c
fid = fopen('newData.txt','wt'); %# Open the file
fprintf(fid,'%g\t %g\t %g\n',[a time c].'); %'# Write the data to the file
fclose(fid); %# Close the file