I'm trying to write a CMake script that does the following:
Using the execute_process instruction reads the output of a command and stores it into a variable named 'STRING_VARIABLE'. The command that returns something that has both characters and digits in it's name. Something like this: RESULT-v1.2.8-...
I have read this value properly and displayed it on the terminal to confirm this.
Now what I want to do is store the first three digits of this output and store them in 3 other variables: 'FIRST_DIGIT', 'SECOND_DIGIT' and 'THIRD_DIGIT'.
My logic was this:
Using a counter count each time a digit is encountered in a variable name. Each time a digit is encountered store the digit in one of the three variables then increment the counter. The counter counts therefore from 0 to 2 and for each of these 3 values does a store.
Here is the script I wrote:
SET(COUNTER 0)
foreach(LETTER ${STRING_VARIABLE})
if(LETTER EQUAL '0,1,2,3,4,5,6,7,8,9')
if( COUNTER EQUAL 0 ) # if first digit is encountered
list(GET STRING_VARIABLE LETTER FIRST_DIGIT) # store it in FIRST_DIGIT
SET(COUNTER 1)
elseif( COUNTER EQUAL 1 ) # if second digit is encountered
list(GET STRING_VARIABLE LETTER SECOND_DIGIT) # store it in SECOND_DIGIT
SET(COUNTER 2)
else( COUNTER EQUAL 2 ) # if second digit is encountered
list(GET STRING_VARIABLE LETTER THIRD_DIGIT) # store it in THIRD_DIGIT
endif()
endif()
endforeach()
# To check the variables
#message("*****${STRING_VARIABLE}") # OK!
message("*****${FIRST_DIGIT}") # NOT OK :(
As I'm a total beginner in CMake I suppose my problem is at either of the two(or both):
- When looping through the 'STRING_VARIABLE' I used foreach(LETTER) and since my string also contains digits the program may not see them. If that is the mistake with what else should I replace LETTER in order to get each character of the string?
- In the first if where I check if the 'LETTER' is a digit. I think that is the correct syntax altough I'm not sure. Basically what I'm doing there is trying to check if the letter at each index is a digit.
The 'STRING_VARIABLE' prints ok as I said.
However when I try printing the 'FIRST_DIGIT' or any other of the 3(second and third) I get an empty string as a result.
Please help me understand what is wrong in my logic and what I'm doing wrong.
Please help me understand what I'm doing wrong. Thank you.
In case the format is know, you can use string(REGEX REPLACE ...).
Function:
function(get_versions versionstring libname major minor patch)
string(REGEX REPLACE "([A-Za-z0-9_]*)-[vV].*" "\\1" locallibname ${versionstring} )
set(libname ${locallibname} PARENT_SCOPE)
string(REGEX REPLACE "^([A-Za-z0-9_]*-[vV])([0-9]*)([.][0-9]*[.][0-9]*-?.*)$" "\\2" numbers ${versionstring} )
set(major ${numbers} PARENT_SCOPE)
string(REGEX REPLACE "^([A-Za-z0-9_]*-[vV][0-9]*[.])([0-9]*)([.][0-9]*-?.*)$" "\\2" numbers ${versionstring} )
set(minor ${numbers} PARENT_SCOPE)
string(REGEX REPLACE "^([A-Za-z0-9_]*-[vV][0-9]*[.][0-9]*[.])([0-9]*)(-?.*)$" "\\2" numbers ${versionstring} )
set(patch ${numbers} PARENT_SCOPE)
endfunction()
Usage:
get_versions("MyLib-V11.222.034-remark" libname major minor patch)
status_ref(libname)
status_ref(major)
status_ref(minor)
status_ref(patch)
Result:
STATUS: libname = "MyLib"
STATUS: major = "11"
STATUS: minor = "222"
STATUS: patch = "034"
Related
I am working on a Automator service and in my situation I have stdin as
B-Funny Flash Nonfiction 202105131635 and I want to get to B-Funny Flash Nonfiction 202105131636 incriminating the "5" by 1 to "6".
I'd think I'd first want to separate the text from the number before doing the add 1 then rejoin them?
Would egrep or sed or awk be best?
Tips?
Bash has simple integer arithmetic built in.
str='B-Funny Flash Nonfiction 202105131635'
# parse into prefix and number
num=${str##*[!0-9]}
prefix=${str%$num}
echo "$prefix$((num+1))"
The parameter expansion ${var#pat} produces the value of the variable var with any prefix matching pat removed; % does the same for suffixes, and doubling the operator changes to matching the longest possible pattern match instead of the shortest. The pattern *[!0-9] matches a string which ends on a character which isn't a number; in this context, it retrieves the prefix, i.e. everything up to just before the first digit. (If your prefix could contain numbers, too, this needs tweaking. Probably switch to removing all digits from the end, then extracting the removed numbers; but I guess this will require an unattractive temporary variable.)
Finally, the secret sauce which evaluates an arithmetic expression is the $((...)) arithmetic context.
For more involved number crunching, try bc or Awk. In fact, this could be a one-liner in Awk:
awk '{ $NF +=1 }1' <<<"$str"
The here string passes the value as standard input to Awk, which increments the last field $NF. The final 1 is a common Awk shorthand for "print all input lines to output".
I don't know the bash tools well enough to give a cool one-line answer, so here is a python script instead.
Usage
Save the code in a file increment.py;
Make the file executable with chmod +x increment.py;
Run the script with ./increment.py blablabla 123.
Code
#!/usr/bin/env python3
import sys
def print_help(argv0):
print('increment numbers by 1')
print('example usage:')
print(' {} B-Funny Flash Nonfiction 202105131635'.format(argv0))
print(' B-Funny Flash Nonfiction 202105131636')
def main(argv):
if len(argv) < 2:
print_help(argv[0])
else:
for s in argv[1:]:
if s.isnumeric():
print(int(s) + 1, end=' ')
else:
print(s, end=' ')
print()
if __name__=='__main__':
main(sys.argv)
Explanation
In a python program called from the command-line, the command-line arguments are stored in the array sys.argv.
The first element of the array, with index 0, is the name that was used to call the program, most likely "./increment.py" in our case.
The remaining elements are the parameters that were passed to the program; the words "B-Funny", "Flash", "Nonfiction", "202105131635" in our case.
The for-loop for s in argv[1:]: iterates on the elements of argv, but starting with the element 1 (thus ignoring the element 0). Each of these elements is a string; the method .isnumeric is used to check whether this string represents a number or not. Refer to the documentation on .isnumeric.
If the string is not numeric, we print is as-is. If the string is numeric, we compute the number it represents by calling int(s), then we add 1, and we print the result.
Apart from that, the line if len(argv): checks whether argv contains at least two elements; if it doesn't, that means it only contains its element 0, which is "./increment.py"; in this case, instead of printing the arguments, the script calls the function print_help which explains how to use the program.
Finally, the bit about if __name__ == '__main__': is a python idiom to check whether the file increment.py was run as a program or as a module imported by another file. Refer to this question.
How can I read the data file containing known number of lines but the number of entries in each line is unknown, e.g. if my data file contain some thing like
1 3 4 5 6 -7 8 -9
1 3 5 6
4 5 6 7 8 3 5 6 7 8 4 5 7 8
i.e. three lines but the data in each line is unknown. At one time I need the data from one line.
One method: read the line into a string, using a string that is at least as long as the longest expected line. Then you go about parsing the string. E.g., if the numbers are always split by spaces, use that to figure out the substring boundaries. Then you can use "internal reads" to read from each sub-string to obtain the numeric values. An internal read uses a string instead of a unit number and obtains the data from the string -- at least you don't have to recreate the conversion of characters to numeric values, the read statement will do that for you. The intrinsic functions provided with Fortran will make the parsing easier.
An implementation based on what M. S. B. pointed out. Quite late, but I guess it could be useful to someone.
Have an array of the type you expect to read ready:
double precision, dimension(MAX_NUM_OF_COLS) :: test_array
Read a line from your file:
READ(reading_unit,'(A)',iostat=io) line
Loop and try to read from the line a maximum quantity of numbers:
do i=1,MAX_NUM_OF_COLS
READ(line, *, iostat=io) test_array(1:i)
if(io==0) exit
enddo
write(*,*) 'number of columns = ', (i-1)
If needed, loop this over all the lines of your file, and keep the maximum or minimum number of columns.
Minimum example:
integer, parameter :: MAX_NUM_OF_COLS=30
integer, parameter :: MAX_LINE_LENGTH=1000
character(len=MAX_LINE_LENGTH) line
integer i, io, reading_unit
double precision, dimension(MAX_NUM_OF_COLS) :: test_array
reading_unit=100
OPEN(reading_unit, file='the_file')
! Get first line of file.
DO
READ(reading_unit,'(A)',iostat=io) line
IF (io/=0) then
write(*,*) "Error reading file."
stop
endif
exit ! Eventually, do not exit and put the DO loop below here.
ENDDO
CLOSE(reading_unit)
do i=1,MAX_NUM_OF_COLS
READ(line,*,iostat=io) test_array(1:i)
if(io==-1) exit
enddo
write(*,*) 'number of columns = ', (i-1)
Assuming that you're okay with padding the array with zeros (specifically referring to the later duplicate question here), this is my idea:
Read the data line by line into a string, then append a number of zeros, finally read each row of the data from this array. Here's an example:
program unknown_numbers
implicit none
integer, parameter :: nrow=3, ncol=14
integer :: data(ncol, nrow)
character(len=2*ncol) :: zeros ! will contain lots of zeros
character(len=10*ncol) :: line ! temporary storage for each line of the file
integer :: i, u
! Write " 0 0 0 0 0 0 0 0" into the string "zeros"
write(zeros, '(*(I2))') [(0, i=1, ncol)]
open(newunit=u, file='data.txt', status='old', action='read')
do i = 1, nrow, 1
! Read the next line into a temporary string array
read(u, '(A)') line
! Append a number of zeros to the temporary string
line = trim(line) // zeros
! Read the row of data from the string.
read(line, *) data(:, i)
end do
close(u)
! For testing purposes, print the data.
print '(14(X, I3))', data
end program unknown_numbers
integer,parameter :: reclen=99999 ! maximum record length
integer,parameter :: undef=-9999 ! undefined value
integer :: actual_reclen ! actual record length
integer,dimension(reclen) :: dummy=undef ! dummy array used for reading
integer,dimension(:),allocatable :: a ! final array
open(unit=10,file='sample.txt',form='formatted',access='sequential')
read(unit=10,fmt=*,end=101)(dummy(i),i=1,reclen)
101 close(unit=10)
actual_reclen=COUNT(dummy/=undef)
allocate(a(actual_reclen))
a=dummy(1:actual_reclen)
end
This is a program that can count numbers in a line (or number of columns) but for a line. If you have many lines, you should change it slightly.
program test12
implicit none
integer n,m,i
integer,allocatable::x(:)
open(10,file='C:\Users\user\Desktop\file.txt')
allocate(x(n))
20 n=n+1
deallocate(x)
allocate(x(n))
read(10,*,iostat=m)(x(i),i=1,n)
if (m==-1)then
goto 30
else
rewind 10
goto 20
end if
30 print*,n-1
end
I have MATLAB set to record three webcams at the same time. I want to capture and save each feed to a file and automatically increment it the file name, it will be replaced by experiment_0001.avi, followed by experiment_0002.avi, etc.
My code looks like this at the moment
set(vid1,'LoggingMode','disk');
set(vid2,'LoggingMode','disk');
avi1 = VideoWriter('X:\ABC\Data Collection\Presentations\Correct\ExperimentA_002.AVI');
avi2 = VideoWriter('X:\ABC\Data Collection\Presentations\Correct\ExperimentB_002.AVI');
set(vid1,'DiskLogger',avi1);
set(vid2,'DiskLogger',avi2);
and I am incrementing the 002 each time.
Any thoughts on how to implement this efficiently?
Thanks.
dont forget matlab has some roots to C programming language. That means things like sprintf will work
so since you are printing out an integer value zero padded to 3 spaces you would need something like this sprintf('%03d',n) then % means there is a value to print that isn't text. 0 means zero pad on the left, 3 means pad to 3 digits, d means the number itself is an integer
just use sprintf in place of a string. the s means String print formatted. so it will output a string. here is an idea of what you might do
set(vid1,'LoggingMode','disk');
set(vid2,'LoggingMode','disk');
for (n=1:2:max_num_captures)
avi1 = VideoWriter(sprintf('X:\ABC\Data Collection\Presentations\Correct\ExperimentA_%03d.AVI',n));
avi2 = VideoWriter(sprintf('X:\ABC\Data Collection\Presentations\Correct\ExperimentB_002.AVI',n));
set(vid1,'DiskLogger',avi1);
set(vid2,'DiskLogger',avi2);
end
I'm trying to learn ruby and having a hard time figuring out what each individual part of this code is doing. Specifically, how does the global subbing determine whether two sequential numbers are both one of these values [13579] and how does it add a dash (-) in between them?
def DashInsert(num)
num_str = num.to_s
num_str.gsub(/([13579])(?=[13579])/, '\1-')
end
num_str.gsub(/([13579])(?=[13579])/, '\1-')
() called capturing group, which captures the characters matched by the pattern present inside the capturing group. So the pattern present inside the capturing group is [13579] which matches a single digit from the given set of digits. That corresponding digit was captured and stored inside index 1.
(?=[13579]) Positive lookahead which asserts that the match must be followed by the character or string matched by the pattern inside the lookahead. Replacement will occur only if this condition is satisfied.
\1 refers the characters which are present inside the group index 1.
Example:
> "13".gsub(/([13579])(?=[13579])/, '\1-')
=> "1-3"
You may start with some random tests:
def DashInsert(num)
num_str = num.to_s
num_str.gsub(/([13579])(?=[13579])/, '\1-')
end
10.times{
x = rand(10000)
puts "%6i: %6s" % [x,DashInsert(x)]
}
Example:
9633: 963-3
7774: 7-7-74
6826: 6826
7386: 7-386
2145: 2145
7806: 7806
9499: 949-9
4117: 41-1-7
4920: 4920
14: 14
And now to check the regex.
([13579]) take any odd number and remember it (it can be used later with \1
(?=[13579]) Check if the next number is also odd, but don't take it (it still remains in the string)
'\1-' Output the first odd num and ab a - to it.
In other word:
Puts a - between each two odds numbers.
I'm working with OPE IDs. One file has them with two trailing zeros, eg, [998700, 1001900]. The other file has them with one or two leading zeros for a total length of six, eg, [009987, 010019]. I want to convert every OPE ID (in both files) to an eight-digit string with exactly two leading zeros and however many zeros at the end to get it to be eight digits long.
Try this:
a = [ "00123123", "077934", "93422", "1231234", "12333" ]
a.map { |n| n.gsub(/^0*/, '00').ljust(8, '0') }
=> ["00123123", "00779340", "00934220", "001231234", "00123330"]
If you have your data parsed and stored as strings, it could be done like this, for example.
n = ["998700", "1001900", "009987", "0010019"]
puts n.map { |i|
i =~ /^0*([0-9]+?)0*$/
"00" + $1 + "0" * [0, 6 - $1.length].max
}
Output:
00998700
00100190
00998700
00100190
This example on codepad.
I'm note very sure though, that I got the description exactly right. Please check the comments and I correct in case it's not exactly what you were looking for.
With the help of the answers given by #detunized & #nimblegorilla, I came up with:
"998700"[0..-3].rjust(6, '0').to_sym
to make the first format I described (always with two trailing zeros) equal to the second.