How can I move to a specific position in a txt file and write a value there? Target can be a specific word or bites from origin

31 次查看(过去 30 天)
Hello, I've written a Matlab script that runs some calculation and that outputs the numerical/text results inside a txt file (generated on purpose) according to a specific layout (columns, comments, ...). The script works like a charm, but there is actually something more I would like to achieve: I need to write the result of a math operation in a specific position of the same txt file (which is close to the top/beginning of the txt file) but unfortunately that value to be written is calculated only at the bottom/end of the script. So the variable is not existing when the script should write it in the txt file (in the right position); it is calculated only later, once the script is over and I've reached the end of txt file.
I am thus looking for a function that can be placed at the end of my script code and that - once the script is over - moves the cursor from the bottom/top to a specific position of the txt file, gets a known value from my workspace of variables and writes it down in that specific position. The specific position can be either identified by a word or by 'X' bites from origin of file.
I was optmistic about "fseek", but either I am messing up or it is not the function I should use.
Please advise !
  5 个评论
per isakson
per isakson 2018-4-16
编辑:per isakson 2018-4-17
Sorry, I should have linked to: memmapfile, Create memory map to a file. memmap is over-kill.
Hint:
ffs = 'c:\tmp\test.txt';
[fid,msg] = fopen( ffs, 'w' );
fprintf( fid, '%s', '1234567890----------1234567890' );
fclose( fid );
mmf = memmapfile( ffs, 'Writable',true );
reshape( mmf.Data, 1,[] )
mmf.Data(11:20) = uint8('abcdefghij');
clear('mmf')
type(ffs)
outputs
ans =
49 50 51 52 53 54 55 56 57 48 ...
1234567890abcdefghij1234567890
I'm a bit confused by the two bytes per character in Matlab vis-a-vis the one in the file. However, the above seems to work with "us-ascii".

请先登录,再进行评论。

回答(5 个)

Walter Roberson
Walter Roberson 2018-4-14
You indicated that this is a text file. Formally speaking, there is no operation for seeking by offset in a text file. The only supported operation (other than beginning or end of file) is to ftell() to get a token representing the current position, after which you can fseek to the token to go back there.
fseek by absolute or relative position is only supported for binary files, not for text files.
The main difference between binary and text files is in the treatment of bytes that match the line terminator. Mostly this has to do with whether carriage return is dropped on input and automatically emitted on output when char(10) is output... Which can happen even for fwrite() if you are in text mode. But operating systems are permitted to do more complicated things in text mode. The specifications were written assuming that you might be writing a text file in the operating system of the WordStar devices that used to exist.
Still, using a text file it would be valid to open in w+ mode and to ftell() right before emitting the placeholder for the missing variable, and then later fseek back and fwrite exactly the same number of bytes as the placeholder making sure that you do emit any newlines when you do.

Maximilian Arpaio
Maximilian Arpaio 2018-4-15
编辑:Maximilian Arpaio 2018-4-15
Thanks to all for the different perspectives.
Actually, I still believe there is a means to do what I am trying to achieve. As an example, I've drafted the simple code below - that basically does what my script aims to do:
clearvars;
fid = fopen('testTriangle.txt','wt');
fprintf(fid,'#THIS IS MY SCRIPT\n');
fprintf(fid,'#IT CALCULATES THE PERIMETER OF A TRIANGLE \n\n');
fprintf(fid,'#THAT HAS AN AREA OF: %4.2f m^2\n', areaT);
A=5.3;
B=6.5;
C=7.1;
Perimeter=A+B+C;
areaT=A*C/2;
fprintf(fid,'#THE PERIMETER OF THE TRIANGLE IS: %4.2f m\n', Perimeter);
%here the "fseek" or similar function should be placed, so as to move the
%cursor to line 5 and provide to the varialbe areaT the info calculated
%only in line 10.
fclose(fid);
Is the above example clarifying the matter ? I can either move the cursor to a specific line or according to a specific word, I'am quite flexible about this. I can also close the file and open it with a different fid if this helps.
Please advise !
  7 个评论
Stephen23
Stephen23 2018-4-15
编辑:Stephen23 2018-4-17
"how should I close this headache ?"
By avoiding it entirely. What you are trying to to is write hack code that messes around counting bytes written in files, something that is ugly and likely to fall apart when someone sneezes. However if you simply move all of the fprintf to the end of the script then this entire problem disappears. Calculate all values, then print to file. Why make it more complex than that?
"Even if I re-arrange the script, I would still face the fact the the comments must stay at the top of the file (like a kind of header) but the data only stay at the bottom. That's the reason why I see no other way in my script that to leave it as it is..."
The order of calculation is totally unrelated to the order that that data can be printed to file. MATLAB does not restrict you to printing the data in the same order that it is created. Create some values, do some calculations, store the values in variables. Print them all when you are finished, in whatever order you wish. No hacking around with file byte counting is required.

请先登录,再进行评论。


dpb
dpb 2018-4-15
编辑:dpb 2018-4-15
>> maxim
>> type testTriangle.txt
#THIS IS MY SCRIPT
#IT CALCULATES THE PERIMETER OF A TRIANGLE OF AREA: 18.81 m^2
#THE PERIMETER OF THE TRIANGLE IS: 18.90 m
>>
>> type maxim
% compute what is wanted/needed...
A=5.3;
B=6.5;
C=7.1;
Perimeter=A+B+C;
areaT=A*C/2;
% NOW write out the results in the desired order
fid = fopen('testTriangle.txt','wt');
fprintf(fid,'#THIS IS MY SCRIPT\n');
fprintf(fid,'#IT CALCULATES THE PERIMETER OF A TRIANGLE OF AREA: %4.2f m^2\n', areaT);
fprintf(fid,'#THE PERIMETER OF THE TRIANGLE IS: %4.2f m\n', Perimeter);
fclose(fid);
>>
Simply rearrange when you do things; wait until it's time; and time is when you have the results required to do the task.
NB:
If the "extended calculations" are indeed of tremendous size and create a huge file and there really is some nontrivial reason can't wait to write earlier results before some final answer drops out at the bottom, the more efficient way to do so is to write that portion of the file independently of the header and when the header information is ready, write a separate header file containing that information that belongs therein.
Then, as the last operation before quitting the script, one simply copies the larger calculations file onto the tail of the header file; iow the two files are combined together to create the final file. This is done most simply by just passing the appropriate command to the operating system.
  2 个评论
dpb
dpb 2018-4-15
It's almost certainly the most wrongheaded way to possibly solve the problem, but...
...
fid = fopen('testTriangle.txt','w');
n=fprintf(fid,'#THIS IS MY SCRIPT\n');
n=n+fprintf(fid,'#IT CALCULATES THE PERIMETER OF A TRIANGLE OF AREA: ');
fprintf(fid,'%4.2f m^2\n', 99.99);
fprintf(fid,'#THE PERIMETER OF THE TRIANGLE IS: %4.2f m\n', Perimeter);
% Absolutely wrongheaded, but...
fseek(fid,n,'bof');
fprintf(fid,'%4.2f',areaT);
fclose(fid);
will overwrite the '99.99' placeholder value; all the preceding issues exist in trying to make this work in general, but, yes, you can write on top of a sequential file at any point within it but it is terribly fraught with "there be dragons!" and as the opening comment says...
Maximilian Arpaio
Maximilian Arpaio 2018-4-16
Thanks dpb,
I've tried your code but the comment is added at the bottom of my (generated) file. My fault, but I forgot to highlight the fact that the script calls a "for" cycle and writes some values in the file. So when I place at the bottom of my file your code:
n=fprintf(fid,'#THIS IS MY SCRIPT\n');
n=n+fprintf(fid,'#IT CALCULATES THE PERIMETER OF A TRIANGLE OF AREA: ');
fprintf(fid,'%4.2f m^2\n', 99.99);
fprintf(fid,'#THE PERIMETER OF THE TRIANGLE IS: %4.2f m\n', Perimeter);
text is place at the bottom, which is something that I don't want. Furthermore, this proves that I cannot rearrange this time....

请先登录,再进行评论。


Walter Roberson
Walter Roberson 2018-4-15
编辑:Walter Roberson 2018-4-16
width_for_placeholder = 20;
fid = fopen('testTriangle.txt','w+t'); %notice change
fprintf(fid,'#THIS IS MY SCRIPT\n');
fprintf(fid,'#IT CALCULATES THE PERIMETER OF A TRIANGLE \n\n');
fprintf(fid,'#THAT HAS AN AREA OF: '); % I want to place here the value of the variable
cursor = ftell(fid);
fwrite(fid, uint8(blanks(width_for_placeholder))); %reserved for "areaT"
fprintf(fid, '\n');
A=5.3;
B=6.5;
C=7.1;
Perimeter=A+B+C;
areaT=A*C/2;
fprintf(fid,'#THE PERIMETER OF THE TRIANGLE IS: %4.2f m\n', Perimeter);
fseek(fid, cursor, 'bof');
fmt_areat = uint8( sprintf('%*.10g', width_for_placeholder, areaT) ); %g format is good about respecting widths no matter what the range of values
%it is important that the output be exactly the reserved length!
if length(fmt_areat) ~= width_for_placeholder
fmt_areat = repmat(uint8('*'), 1, width_for_placeholder);
fprintf('Warning: area value overflowed available width, replaced with **');
end
fwrite(fid, fmt_areat);
%do NOT output a newline here!!
fclose(fid);
The uint8() part is so that we are reserving an exact number of bytes no matter what the character encoding.
Note: if the character encoding is UTF-16 then the area will not be written out in UTF-16. It would be possible to do so, by adding a couple of extra lines.

Maximilian Arpaio
Maximilian Arpaio 2018-4-16
Thanks to all. I will go trough all the different advices and test your lines with my final code. As soon as I have a feedback, I will come back to you in the next hours. Cheers

类别

Help CenterFile Exchange 中查找有关 Low-Level File I/O 的更多信息

标签

产品

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by