1

I have a GUI with a UITable (built in GUIDE). I read in two numerical values and through a series of steps convert the floating-point value to a string. I want the user to have the ability to click on a specific cell in the UITable containing the two values (now as strings), and read those values back as floating-point values. For whatever reason, I can only ever get my code to read in the first floating-point value. My code (in order) is below.

Step 1: Access data and convert to string and place string in corresponding column.

function fillAnnotRangeInfo(obj, selectedAxes)

    selectedAxesTag = selectedAxes.Tag; 
    rawAxesTag = obj.rawDataDisplayAxes.Tag; 
    psdAxesTag = obj.psdDataDisplayAxes.Tag; 

    % Depending on which axes the user clicks on, the units will either be Hz or s 
    if strcmp(selectedAxesTag, rawAxesTag)
        dataRange = strcat(num2str(obj.t1), {'s'}, {' - '}, num2str(obj.t2), {'s'});
    elseif strcmp(selectedAxesTag, psdAxesTag)
        dataRange = strcat(num2str(obj.t1), {'Hz'}, {' - '}, num2str(obj.t2), {'Hz'}); 
    end 

    obj.nextRow.AnnotRange = dataRange; 

end  

Step 2: Determine if user clicked in correct cell and attempt to read two floating-point values out.

% --- Executes when selected cell(s) is changed in existingAnnotationsTable.
function existingAnnotationsTable_CellSelectionCallback(hObject, eventdata, handles)
% hObject    handle to existingAnnotationsTable (see GCBO)
% eventdata  structure with the following fields (see MATLAB.UI.CONTROL.TABLE)
%   Indices: row and column indices of the cell(s) currently selecteds
% handles    structure with handles and user data (see GUIDATA)

% AE = handles.UserData; 

Indices = eventdata.Indices; 

% Determine if column is column of interest 
if Indices(2) == 4
    rangeData = handles.existingAnnotationsTable.Data;
    rangeData = rangeData{Indices(1), Indices(2)}; 
    annoRange = sscanf(rangeData, '%f')
else

end


return

Ultimately, the result I get is if I have a string exactly as follows: "7.4250Hz - 32.502Hz" (or replace Hz with "s"), my program will only produce "7.4250". Nothing more nothing less. I've tried textscan, sscanf, and strread. Each one I explicitly set my filter to floating-point (%f). With strread I tried setting it to cycle through more than once (strread('string', %f, 2)). I have no idea what else to do or try or change.

REGARDING THE ANSWERS BELOW FOR FUTURE READERS: Technically any one of the answers is "correct" (I tried them). They all are applicable to a slightly different situation. Ben's answer is good for getting a result in a single step if you have a fixed format. My own answer works to break down a string across multiple steps giving access to the data each step (useful for performing multiple operations) whilst still being able to deal with varying formats. Andras' answer is useful for getting it done in one step providing a result instantly whilst still being able to deal with varying formats.

DeeWBee
  • 695
  • 4
  • 13
  • 38

3 Answers3

2

Have a look at the sscanf documentation here, if you want a set of floats from a string you need to specify the format. Here is an example from this page similar to yours:

tempString = '78°F 72°F 64°F 66°F 49°F';

degrees = char(176);
tempNumeric = sscanf(tempString, ['%d' degrees 'F'])'
tempNumeric =
    78    72    64    66    49

In your specific case you could try:

val = sscanf(rangedata,'%fHz - %fHz')
Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175
1

Ben's answer is the correct answer in the case of a fixed format. However, in my case, my format changes. Therefore my proposed solution (tested and verified) is to use strtokto "break-down" the string into two separate strings, making it much more manageable and easier to parse. Code below. Thoughts?

% --- Executes when selected cell(s) is changed in existingAnnotationsTable.
function existingAnnotationsTable_CellSelectionCallback(hObject, eventdata, handles)
% hObject    handle to existingAnnotationsTable (see GCBO)
% eventdata  structure with the following fields (see MATLAB.UI.CONTROL.TABLE)
%   Indices: row and column indices of the cell(s) currently selecteds
% handles    structure with handles and user data (see GUIDATA)

% AE = handles.UserData; 

Indices = eventdata.Indices; 

% Determine if column is column of interest 
if Indices(2) == 4
    rangeData = handles.existingAnnotationsTable.Data;
    rangeData = rangeData{Indices(1), Indices(2)}; 

    [dataStart, dataRemain] = strtok(rangeData); 
    % In this case, since there will be just a '-' as the token...
    % ...we don't care about the token and only save the remainder. 
    [~, dataEnd] = strtok(dataRemain); 
    dataStart = sscanf(dataStart, '%f') 
    dataEnd = sscanf(dataEnd, '%f')

else

end
DeeWBee
  • 695
  • 4
  • 13
  • 38
  • 1
    @AndrasDeak I did actually and that only still returns the first floating point value. Could it have to do with the fact that there is a white space on either side of the dash in my format? ("7.4250Hz - 32.502Hz") – DeeWBee Aug 13 '15 at 22:45
  • 1
    I'm afraid you're right (I just checked it for a single word in between). More precisely: you have the first `Hz` and then `-` counting as *two* strings between your numbers. You could try something nastier with `regexprep` to change the string in between (like removing the minus from in between if only your units change), but that wouldn't be more elegant than your current solution, and would probably be slower too (I don't know about `strtok`, but regexp is usually very bulky). – Andras Deak -- Слава Україні Aug 13 '15 at 22:51
1

Motivated by my own comment about using regexp before sscanf, here's a solution which only uses the former:

str=regexp(data,'(?<dataStart>[\d\.]+)[^\d\.]+(?<dataEnd>[\d\.]+)','names');

after which str will have the fields dataStart and dataEnd as strings (so you'll need num2str afterwards). Note that this only works for simple floating point numbers (but of course the regexp could be further complicated ad nauseam if necessary). The upside is that it can be adapted to more tricky input strings, for instance the above will treat any number and kind of non-numeric (and non-dot) text between the first two numbers. The downside is regexp.