Replace text in a struct with new values
12 views (last 30 days)
Show older comments
I have a struct where each row is a line from a text file. I am trying to replace values in a given row with new ones.
idx = 1;
new_value = 0.6411;
text_file(idx).text = '* .57735 .57735 .57735';
text_file(idx).text = strrep(text_file(idx).text,num2str(cell2mat(textscan(text_file(idx).text(9:25),'%f'))),num2str(new_value));
I am trying to keep the struct formatted like the text file so it can be written out into a new file when the replacement is complete. When I run these lines it doesn't change the value
0 Comments
Answers (1)
dpb
on 16 Oct 2021
The problem is the string you're building for the substitution doesn't match the actual string in the struct substring owing to the peculiarities of C printf formatting string behavior for floating point values (and hence for fprintf and friends in MATLAB which are derived from the C i/o RTL).
Illustration of problem follows --
>> text_file(idx).text(9:25) % the substring you're selecting
ans =
'.57735 .'
>> num2str(cell2mat(textscan(text_file(idx).text(9:25),'%f'))) % what your conversion to string produces
ans =
'0.57735'
>>
num2str and all variants based on the C conversion formatting produce the leading 0 in front of the decimal point. This means that string doesn't match the string in the text which is
>> text_file(idx).text
ans =
'* .57735 .57735 .57735'
>>
The most brute-force but least modification to the existing code to make it work would be
>> strrep(text_file(idx).text,strrep(num2str(cell2mat(textscan(text_file(idx).text(9:25),'%f'))),'0.','.'),num2str(new_value,5))
ans =
'* 0.6411 0.6411 0.6411'
>>
which just does an internal string replacement of "0." with "." first.
I'd suggest the above can be simplified some by using the low-level sscanf instead of textscan
>> strrep(text_file(idx).text,strrep(num2str(sscanf(text_file(idx).text(9:25),'%f')),'0.','.'),num2str(new_value))
ans =
'* 0.6411 0.6411 0.6411'
>>
which gets rid of the cell returned by textscan in lieu of the double directly.
Alternatively, use some of the new-fangled string functions --
>> strrep(text_file(idx).text,extractBetween(text_file(idx).text(9:25),'.',' '),extractAfter(string(new_value),'.'))
ans =
"* .6411 .6411 .6411"
>>
2 Comments
dpb
on 16 Oct 2021
Edited: dpb
on 16 Oct 2021
OBTW, if you keep the first solution, NB: that you've then introduced a leading "0." into the file that wasn't there before and then your substitution pattern removing it will end up inserting a second as
strrep(text_file(idx),'.6411','0.1234')
equivalent for a next time through case effective string would result in
0.6411 --> 0.0.1234
because it is replacing only the .6411 substring but the new string without a fixup will have the 0. leading substring in it. Again, the brute-force solution to that is to wrap the formatting expression in the strrep() to eliminate the leading zero same as for the substituted string. I didn't bother for the example above.
If, as it appears to be, this is a fixed-width formatted text file, I think instead of doing what you're doing I'd save it as a straight double array instead of text and rewrite it in its entirety with the desired formatting when needed.
Then all the substitutions can be done numerically with logical addressing and all the string manipulations go away completely.
dpb
on 16 Oct 2021
Or, alternatively to the above, one could keep the numeric array and then just do blanket character substitution into the text file image of the indexed locations to change overwriting the full field width instead of doing character substitution.
The above just treating as numeric and writing on demand still seems the simpler solution without seeing the full problem to be solved, however.
See Also
Categories
Find more on Characters and Strings in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!