Split string repeat Tcl - string

I am a fresh learner of Tcl and I faced an issue of understanding this whole concept:
<name of variable> set [split "[string repeat "-,-," [columns]]-",]
columns is a variable with value 6;
How the split will be and which is my whole string?
Thank you all

<name of variable> set [split "[string repeat "-,-," [columns]]-",]
You have to unpack Tcl commands from the inside out because the inner-most nested brackets are executed first.
columns is a proc that, hopefully, returns an integer.
then string repeat repeats "-,-," that many times.
then the double quoted string adds a trailing -
then split should split that "-,-,-,...-" string on commas resulting in *a list of "2 * columns + 1" hyphens*.
Except:
there is a missing space before the last comma in the split command
the set command looks like: set varname value (unless you're dealing with an object)
set <name of variable> [split "[string repeat "-,-," [columns]]-" ,]
# ...............................................................^
Demonstrating:
set columns 6
proc columns {} {return $::columns}
set result [split "[string repeat "-,-," [columns]]-" ,]
puts $result
puts [llength $result] ;# should be 13
- - - - - - - - - - - - -
13
You could achieve the same result with:
set result [lrepeat [expr {2 * [columns] + 1}] "-"]
Tcl is actually a very simple language. The entire syntax only has 12 rules: https://www.tcl.tk/man/tcl8.6/TclCmd/Tcl.htm

Related

tcl search and replace

I have an input file and I would like to do a search/replace and multiply and dump out the output file. How do I do that in TCL?
All the digit needs to be multiply by a multiplier of 10.
It needs to look for SECTION and END SECTION and find the word 'shape' and multiply all the digit with 10.
Input File
heading
size 9 XY 9
section1
shape name 1 2 3 4
end section1
section 2
shape name 1 2 3 4
end section2
Output file:
heading
size 90 XY 90
section1
shape name 5 10 15 20
end section1
section 2
shape name 100 200 300 400
end section2
tcl
set multiplier1 10
set multiplier2 5
set multiplier3 100
while {[gets $infile1] > 0} {
if {[regexp "size" $value]} {
}
Firstly, you'd be much better off defining the multipliers as an array. Using variable-named variables is usually a bad idea (unless you're about to upvar them). Also, remember that they're associative arrays, so you can use any string as an index and not just numbers; that's sometimes useful.
set multiplier(1) 10
set multiplier(2) 5
set multiplier(3) 100
Secondly, doing the multiplications for a line of numbers is best with a helper procedure:
proc ApplyMultiplies {line multiplier} {
set NUMBER_RE {-?\d+}
# For all locations of numbers, in *reverse* order
foreach location [lreverse [regexp -all -indices -inline -- $NUMBER_RE $line]] {
# Get the number
set value [string range $line {*}$location]
# Multiply it
set value [expr {$value * $multiplier}]
# Write it back into the string
set line [string replace $line {*}$location $value]
}
return $line
}
Testing that interactively:
% ApplyMultiplies {shape name 1 2 3 4} 5
shape name 5 10 15 20
% ApplyMultiplies "tricky_case\"123 yo" 17
tricky_case"2091 yo
In Tcl 8.7, you'll instead be able to do this as a one-liner because of the new -command option to regsub:
proc ApplyMultiplies {line multiplier} {
regsub -all -command -- {-?\d+} $line [list ::tcl::mathop::* $multiplier]
}
I do not understand the conditions under which you are deciding whether to apply the operation. Are the indices to multiplier meant to be section names, but are somehow a bit off? Why are we multiplying values on the size line? Without understanding that, writing the outer control code is impossible for me.

Is there a way to replace characters in a string from index 0 to index -4 (i.e. all but last 4 characters) with a '#'

For example, If my string was 'HelloWorld'
I want the output to be ######orld
My Code:
myString = 'ThisIsAString'
hashedString = string.replace(string[:-4], '#')
print(hashedString)
Output >> #ring
I expected the output to have just one # symbol since it is replacing argument 1 with argument 2.
Can anyone help me with this?
You could multiply # by the word length - 4 and then use the string slicing.
myString = 'HelloWorld'
print('#' * (len(myString) - 4) + myString[-4:])
myString = 'ThisIsAString'
print('#' * (len(myString) - 4) + myString[-4:])
string.replace(old, new) replaces all instances of old with new. So the code you provided is actually replacing the entire beginning of the string with a single pound sign.
You will also notice that input like abcdabcd will give the output ##, since you are replacing all 'abcd' substrings.
Using replace, you could do
hashes = '#' * len(string[:-4])
hashedString = string.replace(string[:-4], hashes, 1)
Note the string multiplication to get the right number of pound symbols, and the 1 passed to replace, which tells it only to replace the first case it finds.
A better method would be to not use replace at all:
hashes = '#' * (len(string) - 4)
leftover = string[-4:]
hashedString = hashes + leftover
This time we do the same work with getting the pound sign string, but instead of replacing we just take the last 4 characters and add them after the pound signs.

Powershell .split and append while keeping split format

Hello my question is how do i keep the format for a string that has had the .split run on it. What i want
$test="a.b.c.d.e"
$test2="abc"
#split test
#append split to test2
#desired output
abc
a
b
c
d
e
I know if i perform split on a string such as
$test="a.b.c.d.e"
$splittest=$test.split(".")
$splittest
#output
a
b
c
d
e
However when i try to make it so that i want to append the above split to a string
$test2="abc"
$test2+$splittest
#output
abca b c d e
while
$splittest+$abc
#output
a
b
c
d
e
abc
Is there a way to append the split string to another string while keeping this split format or will i have to foreach loop through the split string and append it to the $test2 string one by one.
foreach ($line in $splittest)
{
$test2="$($test2)`n$(splittest)"
}
I would prefer not to use the foreach method as it seems to slow down a script i am working on which requires text to be split and appended over 500k times on the small end.
What you're seeing is the effect of how PowerShell's operator overload resolution.
When PowerShell sees +, it needs to decide whether + means sum (1 + 1 = 2), concatenate (1 + 1 = "11"), or add (1 + 1 = [1,1]) in the given context.
It does so by looking at the type of the left hand side argument, and attempts to convert the right hand side argument to a type that the chosen operator overload expects.
When you use + in the order you need, the string value is to the left, and so it results in a string concatenation operation.
There are multiple ways of prepending the string to the existing array:
# Convert scalar to array before +
$newarray = #($abc) + $splittest
# Flatten items inside an array subexpression
$newarray = #($abc;$splittest)
Now all you have to do is join the strings by a newline:
$newarray -join [System.Environment]::NewLine
Or you can change the output field separator ($OFS) to a newline and have it joined implicitly:
$OFS = [System.Environment]::NewLine
"$newarray"
Finally, you could pipe the array to Out-String, but that will add a trailing newline to the entire string:
#($abc;$splittest) |Out-String

list of all the permutations and combinations for 2 strings

Lets take a word
qwerty
What I want is I need to insert periods (dots .) between the string. It can be any other character also.
For example,
q.werty
qw.erty
qwe.rty
qwer.ty
qwert.y
The above is for 1 period or dot. So 1 period combination for a 5 letter string will generate 5 outputs. (N-1)
Now for 2 periods (2 dots) (2 examples only):
q.w.erty
q.we.rty
q.wer.ty
q.wert.y
qw.e.rty
qw.er.ty
qw.ert.y
qwe.r.ty
qwe.rt.y
qwer.t.y
and so on..
NOTE: There must not be 2 consecutive dots between 2 letters in the string. Also, there must not be a period before starting character and/or after ending character.
Can anyone provide a Shell Script (sh, bash) for the above to list all the possible combinations and permutations. I have tried Googling and didn't find any worthwhile content to refer.
EDIT: Any help on how to start this on bash shell script would be great...
Your puzzle is fun so here's a code:
#!/bin/bash
t=qwerty
echo '---- one dot ----'
for (( i = 1; i < ${#t}; ++i )); do
echo "${t:0:i}.${t:i}"
done
echo '---- two dots ----'
for (( i = 1; i < (${#t} - 1); ++i )); do
for (( j = i + 1; j < ${#t}; ++j )); do
echo "${t:0:i}.${t:i:j - i}.${t:j}"
done
done
Output:
---- one dot ----
q.werty
qw.erty
qwe.rty
qwer.ty
qwert.y
---- two dots ----
q.w.erty
q.we.rty
q.wer.ty
q.wert.y
qw.e.rty
qw.er.ty
qw.ert.y
qwe.r.ty
qwe.rt.y
qwer.t.y
See the Bash Manual for everything.
I won't write the code, but I can guide you to the answer.
I assume you want to consider all possible number of dots, not just 1 or 2, but 3, 4, ... , up to the length of the string - 1.
For each character in the string up until the last, there are two possibilities: there is a dot or there is not a dot. So for an n character string, there are O(2^(n-1)) possibilities.
You could write a for loop that goes through all 2^(n-1) possibilities. Each one of these corresponds to a single output with dots after letters.
Let i be an iteration of the for loop. Then have an internal j loop that goes 1 to n-1. If the jth bit is 1, then put a dot after the jth letter.

How do I read a delimited file with strings/numbers with Octave?

I am trying to read a text file containing digits and strings using Octave. The file format is something like this:
A B C
a 10 100
b 20 200
c 30 300
d 40 400
e 50 500
but the delimiter can be space, tab, comma or semicolon. The textread function works fine if the delimiter is space/tab:
[A,B,C] = textread ('test.dat','%s %d %d','headerlines',1)
However it does not work if delimiter is comma/semicolon. I tried to use dklmread:
dlmread ('test.dat',';',1,0)
but it does not work because the first column is a string.
Basically, with textread I can't specify the delimiter and with dlmread I can't specify the format of the first column. Not with the versions of these functions in Octave, at least. Has anybody ever had this problem before?
textread allows you to specify the delimiter-- it honors the property arguments of strread. The following code worked for me:
[A,B,C] = textread( 'test.dat', '%s %d %d' ,'delimiter' , ',' ,1 )
I couldn't find an easy way to do this in Octave currently. You could use fopen() to loop through the file and manually extract the data. I wrote a function that would do this on arbitrary data:
function varargout = coltextread(fname, delim)
% Initialize the variable output argument
varargout = cell(nargout, 1);
% Initialize elements of the cell array to nested cell arrays
% This syntax is due to {:} producing a comma-separated
[varargout{:}] = deal(cell());
fid = fopen(fname, 'r');
while true
% Get the current line
ln = fgetl(fid);
% Stop if EOF
if ln == -1
break;
endif
% Split the line string into components and parse numbers
elems = strsplit(ln, delim);
nums = str2double(elems);
nans = isnan(nums);
% Special case of all strings (header line)
if all(nans)
continue;
endif
% Find the indices of the NaNs
% (i.e. the indices of the strings in the original data)
idxnans = find(nans);
% Assign each corresponding element in the current line
% into the corresponding cell array of varargout
for i = 1:nargout
% Detect if the current index is a string or a num
if any(ismember(idxnans, i))
varargout{i}{end+1} = elems{i};
else
varargout{i}{end+1} = nums(i);
endif
endfor
endwhile
endfunction
It accepts two arguments: the file name, and the delimiter. The function is governed by the number of return variables that are specified, so, for example, [A B C] = coltextread('data.txt', ';'); will try to parse three different data elements from each row in the file, while A = coltextread('data.txt', ';'); will only parse the first elements. If no return variable is given, then the function won't return anything.
The function ignores rows that have all-strings (e.g. the 'A B C' header). Just remove the if all(nans)... section if you want everything.
By default, the 'columns' are returned as cell arrays, although the numbers within those arrays are actually converted numbers, not strings. If you know that a cell array contains only numbers, then you can easily convert it to a column vector with: cell2mat(A)'.

Resources