Escape characters in compose with array input and minus sign *edit* with "empty" string ""

I have trouble understanding the conditions under which compose() works when passing combination of "empty" strings "", escape characters, and normal stuff that you would send to compose:
q = compose("%s %d\\gamma","",4) % i understand this works, and I am fine to implement in my code
q = " 4\gamma"
q = compose("%s%d\\gamma"," ",4) % i understand this works, and I am fine to implement in my code
q = " 4\gamma"
try
q = compose("%s%d\\gamma","",4) % why doesn't this work?
catch ME
ME
end
ME =
MException with properties: identifier: 'MATLAB:printf:BadEscapeSequenceInFormat' message: 'Escaped character '\g' is not valid. See 'doc sprintf' for supported special characters.' cause: {} stack: [3×1 struct] Correction: []
It is still not really an issue for my ultimate goal, as I could rely later on trim() etc., but I posted because it was not easy for me to arrive at the example that actually works because the problem was not obvious to me - and it still isn't despite the hint by dpb's first answer, which identifies the "empty" string as potentially culprit.
q = compose("%s%d","",4) % not an issue with "" here
q = "4"
q = compose("%s\\gamma","") % not an issue with "" even if its right next to escape character
q = "\gamma"
So it doesn't appear to be an issue with passing "" with escape characters, per se...
Some more experiments that illustrate but don't help me understand:
q = compose("%s\\gamma",compose("%s%d","",4)) % this works
q = "4\gamma"
q = compose("%s%s","",compose("%d\\gamma",4)) % this works
q = "4\gamma"
q = compose("%s%s\\gamma","4","") % this works, but doesn't matter for my application because its reversed
q = "4\gamma"
q = compose("%s%s%s","","4","\gamma") % this works, and is potentially the most intuitive solution that works for me
q = "4\gamma"
q = compose("%s%s\\gamma","","4") % this does not work
Error using compose
Escaped character '\g' is not valid. See 'doc sprintf' for supported special characters.
****** original post below when I thought the issue had to do with arrays, which thanks to dpb's answer, it is clearly not:
I am having trouble using "compose()" with arrays, escape characters, and a minus sign at the same time
Compose with arrays with minus signs
s = compose("%s%d/%d",["";"-";"-";""],transpose(1:4),transpose(5:8))
Compose with escape characters with minus sign and scalars
r = compose("%s%d\\gamma/%d",["-"],5,6)
But apparently I cannot combine these 2 things I want to do unless I put a space between the minus sign and the next thing; also no problem with just a minus sign in the format string
q = compose("%s %d\\gamma/%s",["";"-";"-";""],transpose(1:4),compose("%d",transpose(5:8)))
q = compose("-%d\\gamma/%s",transpose(1:4),compose("%d",transpose(5:8)))
q = compose("%s%d\\gamma/%s",["";"-";"-";""],transpose(1:4),compose("%d",transpose(5:8)))
Not a big problem for what i want to ultimately do, but I wasted nearly an hour figuring this out and curious what I'm missing - nothing in the doc for compose helps me understand this...

9 commentaires

dpb
dpb le 24 Sep 2023
Modifié(e) : dpb le 24 Sep 2023
It appears to be about how compose internally parses the string owing to being vectorized, one might presume.
You could submit a support request for an interpretation; doubt anybody every really thought of trying to use it purposefully with an empty string and expecting all permutations thereof to work transparently; an empty object isn't the same thing as a zero-length string that you seem to think should be the result; now whether that would have been a better design choice is possibly open for debate.
Not knowing the actual end use it's hard to conjecture how might solve the problem myself, but from what you've shown us I don't see why either the strtrim() or use of '%d' format string and signed integers aren't useable implementations that both build precisely the string result you're example code is wanting.
As I have mentioned, those are indeed valid solutions and in my final application i actually bypassed the issue altogether...it is at this point just of interest on my part, and in case this can save others an agonizing hour.
I think we disagree on whether "" is empty? It is clearly not. You have shown me that it converts to char as empty, but that is not the same as being empty itself. Especially if Matlab is asserting to me that it is not empty, I don't see why I shouldnt expect it to behave as otherwise?
So perhaps like you say, compose is converting my inputs at some point to char arrays and that is causing some problems in its bowels. I am happy to believe that is the answer.
dpb
dpb le 24 Sep 2023
Modifié(e) : dpb le 25 Sep 2023
It's not that "" is empty, it's that compose() of empty is...
I'd venture it's a rare event for a user to deliberately pass a zero-length string or char to compose, so I expect the likelihood of others spending such a time over the particular problem is also approaching zero. :)
I still think it's worthy of an interpretation from TMW as to what would be expected for the use case--it may be a corner case they never thought of or it may be determined to be undesired behavior or (probably most likely) they could add a note to the doc explaining the behavior.
One of the issues I've always had with TMW doc as thorough as it is is that it is descriptive, not normative; there is no single design document that defines the language unequivocally as with Fortran or C Standard so if they don't think to write something down, it isn't possible as you note to determine what expected behavior should be.
Standard languages have an interpretation process for what the Standard interpretation of any questionable case might be; best you can do with MATLAB is make a support request if a staff member doesn't see/respond here.
And oddly,
compose("%s%d\\\\gamma","",4)
ans = "4\gamma"
compose("%s%d\\gamma",65,4)
ans = "A4\gamma"
compose("%s%d\\gamma",'',4)
ans = 0×0 empty string array
compose("%s%d\\gamma"," ",4)
ans = " 4\gamma"
compose("%s %d\\gamma","",4)
ans = " 4\gamma"
compose("%s%d \\gamma","",4)
Error using compose
Escaped character '\g' is not valid. See 'doc sprintf' for supported special characters.
Clearly there is a bug in processing compose(). For example the version with \\\\ should have had \\ on output.
compose("%s%d\\gamma",'',4)
ans = 0×0 empty string array
is the interesting one but strange then that the empty string as input aborts with the error about bad control character...
Walter, dpb, thanks for your comments and observations, I realize now the context behind treating char arrays and strings similarly (the latter being simply a wrapper around the former), which is probably solidified by the demonstration
s = "";
s{:}
ans = 0×0 empty char array
I am then leaning toward Walter's suggestion that this is a bug in compose(), not necessarily an inherent issue on the handling of the empty char, since sprintf has no issue with the truly empty char ''
sprintf('%s%d\\gamma','',4)
ans = '4\gamma'
sprintf('%s%d\\gamma',"",4)
ans = '4\gamma'
compose("%s%d\\gamma",'',4)
ans = 0×0 empty string array
compose() being relatively new, it's probably not too surprising it still has some warts...you going to submit a bug report?
@dpb, yes i submitted one, significantly simplified and focused than my original question, thanks to your and @Walter Roberson's feedback.
FYI heard back from TMW support confirming that the behavior of
compose("%s%d\\gamma","",4)
is unexpected and is a bug.

Connectez-vous pour commenter.

Réponses (1)

q = compose("%s%d\\gamma/%s",[" ";"-";"-";" "],transpose(1:4),compose("%d",transpose(5:8)))
q = 4×1 string array
" 1\gamma/5" "-2\gamma/6" "-3\gamma/7" " 4\gamma/8"
You are passing an empty string to an expression expecting something in the field.
compose('What does empty%s string do normally','')
ans = 0×0 empty cell array
As you see, that results in an empty output which then turns the string you're trying to generate into an invalid control escape sequence, hence the message.
If the blank space is an issue for some reason, then
q = strtrim(compose("%s%d\\gamma/%s",[" ";"-";"-";" "],transpose(1:4),compose("%d",transpose(5:8))))
q = 4×1 string array
"1\gamma/5" "-2\gamma/6" "-3\gamma/7" "4\gamma/8"
You may have had some reason for the above specific syntax beyond just illustration, but
q=compose("%s%d\\gamma/%d",[" ";"-";"-";" "],[1:4].',[5:8].')
q = 4×1 string array
" 1\gamma/5" "-2\gamma/6" "-3\gamma/7" " 4\gamma/8"
eliminates an unnecessary nested call to compose()
Or
S=[1 -1 -1 1];
A=S.*[1:4];
B=[5:8];
q=compose("%d\\gamma/%d",A.',B.')
q = 4×1 string array
"1\gamma/5" "-2\gamma/6" "-3\gamma/7" "4\gamma/8"
to use variables for data and remove from code -- then can change values without modifying code itself.

11 commentaires

Thanks dpb - interesting about the example about "empty" string, but it doesn't behave the same with true "strings" vs 'strings': Matlab doesn't consider "" to be an "empty string".
compose('What does empty%s string do normally','')
ans = 0×0 empty cell array
because
isempty('')
ans = logical
1
but
compose("What does empty%s string do normally","")
ans = "What does empty string do normally"
should not be a problem because
isempty("")
ans = logical
0
But you did help me clarify that the issue has nothing at all to do with arrays, but more about the emptiness--in some sense, but at least not in the sense of isempty(). I still do not understand it, however. I will edit the question with better examples.
PS The nested compose was just me forgetting to undo some experiments when I thought it was something to do with the %d vs %s bit...
There's was a thread about this particular discussion, I can't find it rn (I'll try to find it and like it tomorrow, it's 2 am here rn), but the essence was -
What is the size of
"abcd"
What about
"abc"
then
"ab"
and
"a"
finally
""
Appreciate the input, but in my mind there is no question, the size of "" is [1,1] as matlab tells you
size("")
ans = 1×2
1 1
because it is not an empty string, it is a scalar string array with contents "", which is consistent with not calling it empty
isempty("")
This is different from how cell arrays work, where you are allowed to have truly empty contents, and I guess this is why '' is intrepreted as empty (that was news to me!)
c = {'hi',[],''}
c = 1×3 cell array
{'hi'} {0×0 double} {0×0 char}
I thought this was odd earlier on when strings were first introduced, but I've made my peace with it because otherwise I think it breaks the array-ness of string arrays, which i think is useful. I have not fully embraced yet, but i think "missing" value in various datatypes might help with this.
The last case for a string object is 0 but compose doesn't return a zero-length string but an empty cellstr object for an empty input.
""
ans = ""
whos ans
Name Size Bytes Class Attributes ans 1x1 150 string
compose('%s',"")
ans = 1×1 cell array
{0×0 char}
@dpb, for clarification are you saying that size of
[""]
ans = ""
should be 0??
size("")
ans = 1×2
1 1
it is not zero, it is a valid scalar!
Also with your last example, you are returning a char array by wrapping your format string in single quotes, but to me that is the same as saying cellstr("") should return an empty char array, which it does, and i think that totally makes sense
cellstr("")
ans = 1×1 cell array
{0×0 char}
I am fully on board with "" not being empty.
strings(0)
ans = 0×0 empty string array
string([])
ans = 0×0 empty string array
string({})
ans = 0×0 empty string array
Some of the ways to get empty string arrays.
As the string class object, yes zero-length is something ("yes, we have no bananas today!") but as @Walter Roberson shows, inside a zero-length string is hidden
s=""
s = ""
s{:}
ans = 0×0 empty char array
the empty char() array. All the string class is is a wrapper around the char() base string array.
Undoubtedly inside the bowels of compose() lies sprintf and the string gets dereferenced internally and boom! I've not delved into the code but the hint from the error and the message that refers to it is just too much to ignore and it is what the base C i/o library uses that almost surely is where it gets to eventually...
There's where the "problem" lies mehinks in expecting that to function in all circumstances...
@Stephen23, that's a fun exchange...makes my brain hurt :)
To be clear, my issue here was that I wasn't testing for empty - because I already "knew" that "" isn't empty (as far as I knew at the time), I expected it to behave as any other scalar string would in all contexts, and didn't really understand where my issue was coming from until dpb pointed out "" is empty in the sense that it is internally the same as ''. This might be the source of the issue, but then again it might not be (sprintf doesn't break with empty char input, e.g.) - the bug may be elsehwere. I've made a support request to TMW so we may learn more.
If it's not a bug but a well-defined error, well, that would be interesting too and we will better understand how compose works, and the relationship of the error to the emptiness (or not) of ""
I've come around to your way of thinking that there is a bug in the compose implementation; I was initially caught up with the error message of invalid control field but neglecting that if nothing were prepended to the format string as given it would still be a valid control string and so something internal that is trying to interpret the generated string is/has gone south with the empty character.
Undoubtedly there was never a case in the test suite that had such a characteristic associated with it... :)
@Stephen23, indeed it is and it was your example as well!
Thank you for linking it.

Connectez-vous pour commenter.

Catégories

Produits

Version

R2023b

Community Treasure Hunt

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

Start Hunting!

Translated by