3
votes

How do you convert a string to lowercase in gnuplot?
This is a gnuplot string handling question.

Example:- I wish to check a user typed parameter in a gnuplot script....

if (tolower(ARG2) == "ohms") {.....

so by accepting "ohms", "Ohms", or "OHMS".

The preference is to not need to use an external "system" command so that the script is more portable. My current best solution is

  arg2 = system("awk 'BEGIN { print toupper(\"".ARG2."\") }'")

and then test the new string variable "arg2", but awk (or other program) may not be generally available on a non unix system, making the gnuplot script less portable.

I cannot see any enhanced gprintf % format specifiers that modifies string presentation - it seems gprintf is only for converting values.

3
On my Ubuntu, awk is not capable to upper/lower non-ASCII letters. Are you trying to check if a string contains a substring in Bash? - Wiktor Stribiżew
Or arg2=system("${ARG2,,}") - Wiktor Stribiżew
I am trying to avoid a system call so the gnuplot script might be more portable to non unix systems. In this case ARG1 is a number and ARG2 is the units which might be dB or Ohms, so I need to do different plotting math. Non-ASCII would fail the match regardless of case, so I guess no problem as the script should default to the no units case - in this case. - mjp
if [[ ${ARG2,,} == "ohms" ]]; then echo YES; fi seems to not be gnuplot syntax - I get an error message - expecting expression. - mjp

3 Answers

2
votes

The full function Macro solution (Thanks theozh) has me thinking again of how to implement this as a function. The idea of using a lookup table to convert characters by equating an ordinal number was a great idea. Encapsulating single character case conversion into a function was the start, and then along with recursion, it has made it possible to handle full strings as I had first looked for. I hope this is now a tidy solution for all. Share and enjoy.

# GNUPLOT string case conversion
# string_case.gnu   M J Pot, 14/1/2019

# Index lookup table strings
UCases="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LCases="abcdefghijklmnopqrstuvwxyz"

# Convert a single character
# Char to convert is added to string so it is always found to default other chars
toupperchr(c)=substr( UCases.c, strstrt(LCases.c, c), strstrt(LCases.c, c) )
tolowerchr(c)=substr( LCases.c, strstrt(UCases.c, c), strstrt(UCases.c, c) )

# Convert whole strings
# Conversion first char (or return null), and recurse for the remaining
toupper(s) = s eq ""  ?  ""  :  toupperchr(s[1:1]).toupper(s[2:*])
tolower(s) = s eq ""  ?  ""  :  tolowerchr(s[1:1]).tolower(s[2:*])

Addition: Improved solution

This is a re-work of the recursive case conversion as self contained functions. A little more effort has resolved the excessive stack usage of the first solution. I had only been considering strings of single words when I had the problem. Note:- the single character conversion has been made more robust.

# GNUPLOT string case conversion
# string_case.gnu   M J Pot, 29/1/2019
# toupper(), tolower() functions

# Index lookup table strings
UCases="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LCases="abcdefghijklmnopqrstuvwxyz"

# Convert a single character
# Char to convert is added to string so it is always found to default other chars
# Null strings are returned null
toupperchr(c)= c eq ""  ?  ""  :  substr( UCases.c, strstrt(LCases.c, c), strstrt(LCases.c, c) )
tolowerchr(c)= c eq ""  ?  ""  :  substr( LCases.c, strstrt(UCases.c, c), strstrt(UCases.c, c) )

# Divide & conquer
# A simple linear recursive call uses too much stack for longer strings.
# This undertakes a binary tree division to make the stack growth order log_2(length)
toupper(s) = strlen(s) <= 1 ? toupperchr(s) : toupper( substr(s,1,strlen(s)/2) ) . toupper( substr(s,(strlen(s)/2)+1,strlen(s)) ) 
tolower(s) = strlen(s) <= 1 ? tolowerchr(s) : tolower( substr(s,1,strlen(s)/2) ) . tolower( substr(s,(strlen(s)/2)+1,strlen(s)) ) 
2
votes

I am not aware that gnuplot offers uppercase or lowercase functions. The attempt below is not a function but a macro which converts the string variable w into uppercase or lowercase without external tools as desired from the OP. Characters which are not found in the list are kept unchanged. There is probably room for improvement, but maybe it will still be helpful to somebody.

### gnuplot implementation of uppercase and lowercase
reset session

Cases= "ABCDEFGHIJKLMNOPQRSTUVWXYZ".\
       "abcdefghijklmnopqrstuvwxyz"

uppercase = 'CaseLen=strlen(Cases)/2;\
    wc = ""; \
    do for [i=1:strlen(w)] { \
        tmp1 = substr(w,i,i); \
        tmp2 = strstrt(Cases,tmp1); \
        wc = (tmp2 == 0) ? wc = wc.tmp1 : \
        (tmp2 > CaseLen) ? (tmp2=tmp2-CaseLen, \
        wc.substr(Cases,tmp2,tmp2)) : wc.substr(Cases,tmp2,tmp2);\
    }; w = wc'

lowercase = 'CaseLen=strlen(Cases)/2;\
    wc = ""; \
    do for [i=1:strlen(w)] { \
        tmp1 = substr(w,i,i); \
        tmp2 = strstrt(Cases,tmp1); \
        wc = (tmp2 == 0) ? wc.tmp1 : \
        (tmp2 < CaseLen) ? (tmp2=tmp2+CaseLen, \
        wc.substr(Cases,tmp2,tmp2)) : wc.substr(Cases,tmp2,tmp2);\
    }; w = wc'

# put your string into variable w
w = "...thE qUick brOWn foX jUmPs oVeR The LazY Dog!"
print w
# run the macro uppercase and variable w will we converted to uppercase
@uppercase
print w

# run the macro lowercase and variable w will we converted to lowercase
@lowercase
print w

### end of code

Output:

...thE qUick brOWn foX jUmPs oVeR The LazY Dog!
...THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG!
...the quick brown fox jumps over the lazy dog!

Addition: improved version

I guess that's the spirit of SO... together find the best solutions :-). @mjp, using your nice idea about adding the character at the end and my "new" idea of "misusing" the summation expression sum as a loop, thus avoiding recursion. The follwing solution does not have the limit of 247 characters for the string which the recursive solution has (at least on my computer it was 247). I hope there will be no severe limitations in this solution.

### gnuplot implementation of uppercase and lowercase
# theozh, 14.01.2019
reset session

UpperCases= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LowerCases= "abcdefghijklmnopqrstuvwxyz"

# upper/lowercase for characters
ucchar(c) = substr( UpperCases.c, ucchar_tmp=strstrt(LowerCases.c, c), ucchar_tmp)
lcchar(c) = substr( LowerCases.c, lcchar_tmp=strstrt(UpperCases.c, c), lcchar_tmp)

# upper/lowercase for strings
uc(s) = ((sum[uc_i=1:strlen(s)] (uc_tmp=ucchar(substr(s,uc_i,uc_i)), uc_i>1 ? (uc_w=uc_w.uc_tmp,1):uc_w=uc_tmp,1)),uc_w)
lc(s) = ((sum[lc_i=1:strlen(s)] (lc_tmp=lcchar(substr(s,lc_i,lc_i)), lc_i>1 ? (lc_w=lc_w.lc_tmp,1):lc_w=lc_tmp,1)),lc_w)

s = "...thE qUick brOWn foX jUmPs oVeR The LazY Dog!"
print s
print uc(s)
print lc(s)
### end of code

Edit: (Jan. 2022) I stumbled again over my old post and thought it can be further simplified. And more characters are added to the lookup strings.

Code:

### uppercase/lowercase function for gnuplot
reset session

UpperCases= "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÁÂÀËÉÊÈÏÍÎÌÖÓÔÒÜÚÛÙŸÝ"
LowerCases= "abcdefghijklmnopqrstuvwxyzäáâàëéêèïíîìöóôòüúûùÿý"

# upper/lowercase for characters
ucc(c) = ((ucc_i=strstrt(LowerCases, c)) ? UpperCases[ucc_i:ucc_i] : c)
lcc(c) = ((lcc_i=strstrt(UpperCases, c)) ? LowerCases[lcc_i:lcc_i] : c)

# upper/lowercase for strings
uc(s) = (uc_s='', sum[uc_i=1:strlen(s)] (uc_s=uc_s.ucc(s[uc_i:uc_i]),1),uc_s)
lc(s) = (lc_s='', sum[lc_i=1:strlen(s)] (lc_s=lc_s.lcc(s[lc_i:lc_i]),1),lc_s)

s = "...thE qUick brOWn foX jUmPs oVeR The LazY Dog!"
print "Input:     ", s
print "Uppercase: ", uc(s)
print "Lowercase: ", lc(s)
### end of code

Result:

Input:     ...thE qUick brOWn foX jUmPs oVeR The LazY Dog!
Uppercase: ...THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG!
Lowercase: ...the quick brown fox jumps over the lazy dog!
0
votes

When wishing to compare a string variable independent of case to a known string constant, only a subset of characters need to be converted. This is not an answer to the general situation of case conversion in gnuplot, but may work for many situations.

arg2 = ARG2
# As we want limited comparisons strstrt(”string”, ”key”) can be used....
# Substitute the first occurrence of a character [pat], with another character [repl]
# Can be used to convert an expected word's case, one character at a time
subchr(src, pat, repl) = strstrt(src, pat)?src[*:strstrt(src, pat)-1].repl.src[strstrt(src, pat)+1:*]:src
arg2 = subchr(arg2, "o", "O")
arg2 = subchr(arg2, "h", "H")
arg2 = subchr(arg2, "m", "M")
arg2 = subchr(arg2, "s", "S")
arg2 = subchr(arg2, "d", "D")
arg2 = subchr(arg2, "b", "B")
if ( arg2[1:2] eq "DB" ) {
  # In terms of dB
  .....
  }
else {
  if ( arg2 eq "OHMS" ) {
    .....
    }
  }

The solution is to write a gnuplot string function subchr() which replaces a single matching character, only if found (the ternary ?), and to call it for each of the characters to be converted. Fortunately, gnuplot string range specifiers appear well behaved when indexing before (0) and beyond (stringlength+1), returning null for that region. This allows us to return the string with each character upgraded.

This avoids the need for a system() call to a program like awk, etc.