Tcl split by tabs (\t) - tcl

I am trying to split a Tcl string by TABS (\t).
Please consider the following sampleString:
I . am -> a . programmer # let "." be spaces and "->" be tabs
If I try to do the following:
set myVar [split $sampleString "\t"]
Tcl will split by spaces as well and not just the tabs.
How can I split only by tabs?
Thanks

I suspect you're just a little confused as to which output you are looking at.
% set s "I am\ta programmer"
I am a programmer
% split $s
I am a programmer
% split $s "\t"
{I am} {a programmer}
The only difference between the two splits is that without the optional second argument, the split-set is “all whitespace” (for a reasonable definition of “all”), and neither split affects the value in the variable as there is no explicit write-back here.

Related

How do I define a text area using sed and printf in tcl

Hi im try to add a definded text area %-74s using sed and printf in a tcl script i have but im not sure how to add the printf info to the line of code i have
puts $f "sed -i "s/XXXTLEXXX/\$1/\" /$file";
any help would be greatly appreciated
ive tried a few combinations but all error
Your problem is that you have a need to peint a string with limited substitutions in it, yet that string contains $, " and \ characters in it. Those special characters mean that using a normal double-quoted word in Tcl is very awkward; you could use lots of backslashes to quote the TCL metacharacters, but that's horrible when most of the string is in another language (shell/sed in your case). Here is a better option with string map and a brace-quoted word (which is free of substitutions):
set str {sed -i "s/XXXTLEXXX/$1/" /%FILE%}
puts $f [string map [list "%FILE%" $file] $str]
Note that you can do multiple substitutions in one string map, and that it does each substitution wherever it can. You can use a multi-line literal too. (%FILE% was chosen to be a literal that didn't otherwise occur in the string. Pick your own as you need them, but putting the name in helps with readability.)

Split camelcase value with TCL

I have this TCL expression:
[string toupper [join [lrange [file split [value [topnode].file]] 1 1]]]
This retrieves companyName value from c:/companyName... and I need to split that value before the first capital letter into Company Name. Any ideas?
Thanks in advance.
That's rather more in one word than I would consider a good idea. It makes the whole thing quite opaque! Let's split it up.
Firstly, I would expect the base company name to be better retrieved with lindex from the split filename.
set companyName [lindex [file split [value [topnode].file]] 1]
Now, we need to process that to get the human-readable version out of it. Alas, that's going be a bit difficult without knowing what's been done to it, but if we use as our example fooBarBoo_grill then we can see what we can do. First, we get the pieces with some regular expressions (this part might need tweaking if there are non-ASCII characters involved, or if certain critical characters need special treatment):
# set companyName "fooBarBoo_grill"
set pieces [regexp -all -inline {[a-z]+|[A-Z][a-z]*} $companyName]
# pieces = foo Bar Boo grill
Next, we need to capitalise. I'll assume you're using Tcl 8.6 and so have lmap as it is perfect for this task. The string totitle command has been around for a very long time.
set pieces [lmap word $pieces {string totitle $word}]
# pieces = Foo Bar Boo Grill
That list might need a bit more tweaking, or it might be OK as it is. An example of tweaking that might be necessary is if you've got an Irish name like O'Hanrahan, or if you need to insert a comma before and period after Inc.
Finally, we properly ought to set companyName [join $pieces] to get back a true string, but that doesn't have a noticeable effect with a list of words made purely out of letters. Also, more complex joins with regular expressions might be needed if you've done insertion of prefixing punctuation (the , Inc. case).
If I was doing this for real, I'd try to have the proper company name expressed directly elsewhere rather than relying on the filename. Much simpler to get right!
To begin with, try using
lindex [file split [value [topnode].file]] 1
The lrange command will return a list, which might cause problems with some directory names. The join command should be pointless if you don't use lrange, and string toupper removes the information you need to do the operation you want to do.
To split before uppercase letters, you can use repetitive matches of either (?:[a-z]+|[A-Z][a-z]+) (ASCII / English alphabet letters only) or (?:[[:lower:]]+|[[:upper:]][[:lower:]]+) (any Unicode letters).
% regexp -all -inline {(?:[a-z]+|[A-Z][a-z]+)} camelCaseWord
camel Case Word
Use string totitle to change the first letter of the first word to upper case.
Documentation:
file,
lindex,
regexp,
string,
Syntax of Tcl regular expressions

How to grep parameters inside square brackets?

Could you please help me with the following script?
It is a Tcl script which Synopsys IC Compiler II will source.
set_dont_use [get_lib_cells */*CKGT*0P*] -power
set_dont_use [get_lib_cells */*CKTT*0P*] -setup
May I know how to take only */*CKGT*0P* and */*CKTT*0P* and assign these to a variable.
Of course you can treat a Tcl script as something you search through; it's just a file with text in it after all.
Let's write a script to select the text out. It'll be a Tcl script, of course. For readability, I'm going to put the regular expression itself in a global variable; treat it like a constant. (In larger scripts, I find it helps a lot to give names to REs like this, as those names can be used to remind me of the purpose of the regular expression. I'll call it “RE” here.)
set f [open theScript.tcl]
# Even with 10 million lines, modern computers will chew through it rapidly
set lines [split [read $f] "\n"]
close $f
# This RE will match the sample lines you've told us about; it might need tuning
# for other inputs (and knowing what's best is part of the art of RE writing)
set RE {^set_dont_use \[get_lib_cells ([\w*/]+)\] -\w+$}
foreach line $lines {
if {[regexp $RE $line -> term]} {
# At this point, the part you want is assigned to $term
puts "FOUND: $term"
}
}
The key things in the RE above? It's in braces to reduce backslash-itis. Literal square brackets are backslashed. The bit in parentheses is the bit we're capturing into the term variable. [\w*/]+ matches a sequence of one or more characters from a set consisting of “standard word characters” plus * and /.
The use of regexp has -> as a funny name for a variable that is ignored. I could have called it dummy instead; it's going to have the whole matched string in it when the RE matches, but we already have that in $term as we're using a fully-anchored RE. But I like using -> as a mnemonic for “assign the submatches into these”. Also, the formal result of regexp is the number of times the RE matched; without the -all option, that's effectively a boolean that is true exactly when there was a match, which is useful. Very useful.
To assign the output of any command <command> to a variable with a name <name>, use set <name> [<command>]:
> set hello_length [string length hello]
5
> puts "The length of 'hello' is $hello_length."
The length of 'hello' is 5.
In your case, maybe this is what you want? (I still don't quite understand the question, though.)
set ckgt_cells [get_lib_cells */*CKGT*0P*]
set cktt_cells [get_lib_cells */*CKTT*0P*]

How to append two string in TCL with a space between them?

I'm trying to append two string in tcl. I'm reading from csv and set values to the variables and then i will use that for assigning it my application. I tried the below one.
set vMyvalue [lindex $lsLine 17]
append vMyvalue " [lindex $lsLine 18]"
it is giving me the expected result. Like for e.g if i have values 250 and km in 17th and 18th position in csv. i'm getting
250 km
But the problem is when there are no values in the 17th and 18th i mean when it is empty, that time also it is adding space. But my application won't allow me to assign space for that value. How can i resolve this? I just started working in TCL. I'm not aware of many functions.
I think the most intuitive way to handle cases similar to this one if you don't know a function to do this (including for example if you are joining two strings with some character but if any one of them are empty strings, then you want something different to be done), would be to use if. In this case:
if {$vMyvalue eq " "} {set vMyvalue ""}
If you want to make your code somewhat shorter, you can make use of the functions lrange (list range), join and string:
set vMyvalue [string trim [join [lrange $lsLine 17 18] " "]]
lrange returns a list of elements from the list $lsLine between indices 17 to 18 inclusive, then join literally joins those elements with a space, and last, string trim cleans up any leading and trailing spaces (removing the space completely if it is the only character in the string).
There are several ways to do this. The minimum modification from the code you already have is probably to trim the result. Trim removes leading and trailing whitespace but if it's only whitespace it would trim it to an empty string. So:
set myValue [string trim $myValue]

Use of [list a b c] vs {a b c} when creating a list

What differences are there between creating a list in TCL using:
[list a b c]
vs
{a b c}
I'm by all means not an experienced TCL programmer, but the only difference I have encountered so far is when creating a list of multiple lines the first style requires using line continuation characters like:
[list \
a \
b \
c \
]
where this parses fine:
{
a
b
c
}
Are there any other differences? Which is considered better style or idiomatic?
It would appear that when creating a complex list with nested lists, the 2nd style is the only clean way to go.
The main difference is that using the list command makes it possible to use variables when defining the list. Notice the difference between these two:
% set foo 1
1
% set bar 2
2
% set list1 [list $foo $bar]
1 2
% set list2 {$foo $bar}
$foo $bar
Note that you can also use double quotes if you want:
% set list3 "$foo $bar"
1 2
It's important to note that of the two ways to build a list with variables, only using list is guaranteed to give you a proper list. Using quotes may or may not give you a list, depending on the contents of the variables. This isn't because Tcl is mysterious or random or buggy -- it's simply how tcl quoting works. With list you are asking tcl to construct a list with specific elements, in the others you're creating a string that looks like a list, but whether it can be treated like a list or not depends on the data in the string.
Here's an example where using quotes won't give you a list:
% set foo "{"
{
% set list4 "$foo $bar"
{ 2
% lindex $list4 0
unmatched open brace in list
... whereas using list will give you a proper list:
% set list5 [list $foo $bar]
\{ 2
% lindex $list5 0
{
It's important to know that the backslash appears only when tcl converts the list to a string for the purposes of printing the list -- the backslash isn't in the data, as you can see when you use lindex to fetch the value.
Are there any other differences? Which is considered better style or idiomatic?
In the case where you're after a list with just literals in it, no variable or command expansion or anything like that, using [list a b c] and {a b c} is exactly the same. They compile to identical bytecode in Tcl 8.6 (the push of a literal onto the result stack). There really is no difference at all.
Which is more idiomatic? I don't really know, to be honest. They are both idiomatic, and subject to individual preferences. The difference between them becomes important once you start using variable and command expansion, and then the question quickly ceases to be relevant.
That said, I mostly prefer to use {a b c}. It's more convenient when the literals are longer since I can break things over multiple lines without fussing around with backslash continuations. Other people will disagree with me; for them, the typing of [list…] reminds them strongly of what they intend to do with the data, and that's clearly of some mnemonic value.