• Proper way to split a string?

    From Luc@luc@sep.invalid to comp.lang.tcl on Tue Dec 17 01:37:49 2024
    From Newsgroup: comp.lang.tcl

    Howdy. I have this pop-up dialog that prompts for two pieces of
    information: a name (arbitrary string) and a button that will
    invoke a file browser so the user can select a file. Then:

    bind $::SRw <Return> {set ::PINPUT "$pname|$pfile"}

    then:

    vwait ::PINPUT
    catch {destroy $::SRw}
    return $::PINPUT

    It works.

    But then I use the ::PINPUT variable, splitting it on the pipe |
    symbol to identify the two pieces of information. And then I think,
    what if the user uses the pipe symbol in $name?

    OK, how about this:

    bind $::SRw <Return> {set ::PINPUT "$pname::::::::::::::::$pfile"}

    And then I think, what if the user's cat walks on the keyboard and
    the user actually enters that kind of string?

    OK, how about this:

    bind $::SRw <Return> {set ::PINPUT "$pname|=MagicSeparator=|$pfile"}

    And then I think, what if the user is possessed by Satan and actually
    uses that Magic Separator in $name?

    As unlikely as that is, I ask of you: what is the wise, serious,
    professional way of handling that situation and making sure that
    worse things won't happen at sea?
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Tue Dec 17 05:05:04 2024
    From Newsgroup: comp.lang.tcl

    Luc <luc@sep.invalid> wrote:
    As unlikely as that is, I ask of you: what is the wise, serious, professional way of handling that situation and making sure that
    worse things won't happen at sea?

    If you really want both pieces in the same variable then use a proper
    list:

    bind $::SRw <Return> {set ::PINPUT [list $pname $pfile]}

    Then you can access each piece with lindex 0 or lindex 1 (depending
    upon which you want at the time) and Tcl will take care of making sure
    the two pieces remain separate pieces.

    Otherwise, just store them away as two separate variables:

    bind $::SRw <Return> {
    set ::PINPUT_name $pname
    set ::PINPUT_file $pfile
    }

    Note, your example will likely not work because when the <Return>
    binding runs, $pname and $pfile will not be variables in the global
    scope, so adjust accordingly.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Emiliano@emiliano@example.invalid to comp.lang.tcl on Sat Dec 21 00:41:42 2024
    From Newsgroup: comp.lang.tcl

    On Tue, 17 Dec 2024 01:37:49 -0300
    Luc <luc@sep.invalid> wrote:

    [snip]
    OK, how about this:

    bind $::SRw <Return> {set ::PINPUT "$pname::::::::::::::::$pfile"}

    Incidentally, this wont work.

    % set pname foo
    foo
    % set pfile bar
    bar
    % set ::PINPUT "$pname::::::::::::::::$pfile"
    can't read "pname::::::::::::::::": no such variable

    This is because there are two variable expansions in this expression:
    one is ${pname::::::::::::::::} which is a variable in the (relative to
    current scope) pname namespace with the with the empty string {} as
    name (equivalent to [namespace eval pname {set {}}]) and the other is
    $pfile. As the [namespace] documentation states, two or more colons are namespace separators, so any number of colons > 2 are equivalent to "::".

    Quoting the manual page:
    "Extra colons in any separator part of a qualified name are ignored;
    i.e. two or more colons are treated as a namespace separator.
    A trailing :: in a qualified variable or command name refers to the
    variable or command named {}."

    Regards
    --
    Emiliano
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Gerald Lester@Gerald.Lester@gmail.com to comp.lang.tcl on Fri Dec 20 23:13:44 2024
    From Newsgroup: comp.lang.tcl

    On 12/16/24 22:37, Luc wrote:
    Howdy. I have this pop-up dialog that prompts for two pieces of
    information: a name (arbitrary string) and a button that will
    invoke a file browser so the user can select a file. Then:

    bind $::SRw <Return> {set ::PINPUT "$pname|$pfile"}

    then:

    vwait ::PINPUT
    catch {destroy $::SRw}
    return $::PINPUT

    It works.

    But then I use the ::PINPUT variable, splitting it on the pipe |
    symbol to identify the two pieces of information. And then I think,
    what if the user uses the pipe symbol in $name?

    OK, how about this:

    bind $::SRw <Return> {set ::PINPUT "$pname::::::::::::::::$pfile"}

    And then I think, what if the user's cat walks on the keyboard and
    the user actually enters that kind of string?

    OK, how about this:

    bind $::SRw <Return> {set ::PINPUT "$pname|=MagicSeparator=|$pfile"}

    And then I think, what if the user is possessed by Satan and actually
    uses that Magic Separator in $name?

    As unlikely as that is, I ask of you: what is the wise, serious,
    professional way of handling that situation and making sure that
    worse things won't happen at sea?
    ]

    Rich suggested making it a list. You can do that or make ::PINPUT an array:

    set ::PINPUT(state) waiting
    bind $::SRw <Return> {
    set ::PINPUT(pname) $pname
    set ::PINPUT(pname) $pfile
    set ::PINPUT(state) done
    }

    then:

    vwait ::PINPUT(state)
    catch {destroy $::SRw}
    return [array get ::PINPUT]

    Where you go to use it, do:

    array set resultArray $returnValue

    Or use the dict command to access the pieces.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Luc@luc@sep.invalid to comp.lang.tcl on Sat Dec 21 16:54:27 2024
    From Newsgroup: comp.lang.tcl

    On Fri, 20 Dec 2024 23:13:44 -0600, Gerald Lester wrote:

    Rich suggested making it a list. You can do that or make ::PINPUT an array:

    set ::PINPUT(state) waiting
    bind $::SRw <Return> {
    set ::PINPUT(pname) $pname
    set ::PINPUT(pname) $pfile
    set ::PINPUT(state) done
    }

    then:

    vwait ::PINPUT(state)
    catch {destroy $::SRw}
    return [array get ::PINPUT]

    Where you go to use it, do:

    array set resultArray $returnValue

    Or use the dict command to access the pieces.

    **************************

    It's good to know that I was even wronger than I thought.

    I have been using a list as Rich suggested, but the use of an array
    is very interesting, rather Tcl-ish.

    Thank you all for the education.
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Sat Dec 21 21:12:01 2024
    From Newsgroup: comp.lang.tcl

    Luc <luc@sep.invalid> wrote:
    On Fri, 20 Dec 2024 23:13:44 -0600, Gerald Lester wrote:

    Rich suggested making it a list. You can do that or make ::PINPUT an array: >>
    set ::PINPUT(state) waiting
    bind $::SRw <Return> {
    set ::PINPUT(pname) $pname
    set ::PINPUT(pname) $pfile
    set ::PINPUT(state) done
    }

    then:

    vwait ::PINPUT(state)
    catch {destroy $::SRw}
    return [array get ::PINPUT]

    Where you go to use it, do:

    array set resultArray $returnValue

    Or use the dict command to access the pieces.

    **************************

    It's good to know that I was even wronger than I thought.

    I have been using a list as Rich suggested, but the use of an array
    is very interesting, rather Tcl-ish.

    And array has the downside of you cannot directly pass an array into a
    proc nor directly return an array from a proc [1].

    Either of a list or a dict can be passed in and returned directly with
    no further 'effort' needed on your part within your code.

    A list gives you the data, but without any attached 'names', so you
    have to remember that "index 2 is data value Y" elsewhere.

    A dict gives you 'named values' (just like an array) so if you prefer
    that method, then you can use a dict to 'return' the values out of a
    proc, and they will have names just like with an array.

    And, you can convert a dict to an array via "array set array_name
    $dict_name". Converting an array to a dict works this way "set
    dict_name [dict create {*}[array get array_name]]" or by just
    'accessing' the list returned from [array get] with the [dict] command,
    which will shimmer it into a dict. The explicit [dict create] method
    does make it clear in your code that you are purposefully performing
    the transform.


    [1] Instead you have to do things like "array get name_of_array" to
    return it, or pass in the name of the array and use upvar to access it.


    --- Synchronet 3.20a-Linux NewsLink 1.114