Autohotkey: Splitting a concatenated string into string and number - autohotkey

I am using an input box to request a string from the user that has the form "sometext5". I would like to separate this via regexp into a variable for the string component and a variable for the number. The number then shall be used in a loop.
The following just returns "0", even when I enter a string in the form "itemize5"
!n::
InputBox, UserEnv, Environment, Please enter an environment!, , 240, 120
If ErrorLevel
return
Else
FoundPos := RegExMatch(%UserEnv%, "\d+$")
MsgBox %FoundPos%
retur
n

FoundPos, as its name implies, contains the position of the leftmost occurrence of the needle. It does not contain anything you specifically want to match with your regex.
When passing variable contents to a function, don't enclose the variable names in percent signs (like %UserEnv%).
Your regex \d+$ will only match numbers at the end of the string, not the text before it.
A possible solution:
myText := "sometext55"
if( RegExMatch(myText, "(.*?)(\d+)$", splitted) ) {
msgbox, Text: %splitted1%`nNumber: %splitted2%
}
As described in the docs, splitted will be set to a pseudo-array (splitted1, splitted2 ...), with each element containing the matched subpattern of your regex (the stuff that is in between round brackets).

Related

Converting numbers into timestamps (inserting colons at specific places)

I'm using AutoHotkey for this as the code is the most understandable to me. So I have a document with numbers and text, for example like this
120344 text text text
234000 text text
and the desired output is
12:03:44 text text text
23:40:00 text text
I'm sure StrReplace can be used to insert the colons in, but I'm not sure how to specify the position of the colons or ask AHK to 'find' specific strings of 6 digit numbers. Before, I would have highlighted the text I want to apply StrReplace to and then press a hotkey, but I was wondering if there is a more efficient way to do this that doesn't need my interaction. Even just pointing to the relevant functions I would need to look into to do this would be helpful! Thanks so much, I'm still very new to programming.
hfontanez's answer was very helpful in figuring out that for this problem, I had to use a loop and substring function. I'm sure there are much less messy ways to write this code, but this is the final version of what worked for my purposes:
Loop, read, C:\[location of input file]
{
{ If A_LoopReadLine = ;
Continue ; this part is to ignore the blank lines in the file
}
{
one := A_LoopReadLine
x := SubStr(one, 1, 2)
y := SubStr(one, 3, 2)
z := SubStr(one, 5)
two := x . ":" . y . ":" . z
FileAppend, %two%`r`n, C:\[location of output file]
}
}
return
Assuming that the "timestamp" component is always 6 characters long and always at the beginning of the string, this solution should work just fine.
String test = "012345 test test test";
test = test.substring(0, 2) + ":" + test.substring(2, 4) + ":" + test.substring(4, test.length());
This outputs 01:23:45 test test test
Why? Because you are temporarily creating a String object that it's two characters long and then you insert the colon before taking the next pair. Lastly, you append the rest of the String and assign it to whichever String variable you want. Remember, the substring method doesn't modify the String object you are calling the method on. This method returns a "new" String object. Therefore, the variable test is unmodified until the assignment operation kicks in at the end.
Alternatively, you can use a StringBuilder and append each component like this:
StringBuilder sbuff = new StringBuilder();
sbuff.append(test.substring(0,2));
sbuff.append(":");
sbuff.append(test.substring(2,4));
sbuff.append(":");
sbuff.append(test.substring(4,test.length()));
test = sbuff.toString();
You could also use a "fancy" loop to do this, but I think for something this simple, looping is just overkill. Oh, I almost forgot, this should work with both of your test strings because after the last colon insert, the code takes the substring from index position 4 all the way to the end of the string indiscriminately.

Get First Character of A_LoopFileName?

I'm trying to parse a filename and get the first character from it as a string to compare it to a previously inputted variable. My code looks like:
FileSelectFolder, WhichFolder ; Ask the user to pick a folder.
; Ask what letter you want to start the loop from
InputBox, UserInput, Start At What Letter?, Please enter a letter to start at within the folder (CAPITALIZE IT!)., , 450, 150
if ErrorLevel {
MsgBox, CANCEL was pressed.
ExitApp
} else {
inputted_letter = %UserInput%
tooltip %inputted_letter% ; Show the inputted letter
sleep, 2000
tooltip
}
Loop, %WhichFolder%\*.*
{
current_filename_full = %A_LoopFileName%
files_first_letter := SubStr(current_filename_full, 1, 1)
tooltip %files_first_letter% ; Show the file's first letter
sleep, 2000
tooltip
if files_first_letter != inputted_letter
continue
...
Right now, it clearly shows in the tooltips the user-entered capital letter, and then the first letter of each file name from within the selected folder, but for some reason when the two look alike, it doesn't recognize them as a match. I'm thinking maybe because technically A_LoopFileName is not of a string type? Or maybe the inputted letter doesn't match the type of the first filename's letter?
I want it to continue if the inputted letter and the first letter of the filename don't match, but if they do, to carry on with the rest of the script. Any ideas on how I can get these two to successfully match? Thanks!
Firstly, AHK doesn't really have types. At least not how you've experienced types in other languages.
So your assumption about "not being correct type" will pretty much always be wrong.
So the actual cause is because in a legacy if statement, the syntax is
if <name of variable> <operator> <legacy way of representing a value>
So you'd do it like this:
if files_first_letter != %inputted_letter%
You we're comparing if the variable files_first_letter is equal to the literal text inputted_letter.
However, I highly recommend you stop using legacy syntax. It's really just that old.
It'll differ horribly much from any other programming language and you run into confusing behavior like this. Expression syntax is what you want to use in AHK nowadays.
Here's your code snippet converted over to expression syntax in case you're interested:
FileSelectFolder, WhichFolder
;Forcing an expression like this with % in every parameter
;is really not needed of course, and could be considered
;excessive, but I'm doing it for demonstrational
;purposes here. Putting everything in expression syntax.
;also, not gonna lie, I always do it myself haha
InputBox, UserInput, % "Start At What Letter?", % "Please enter a letter to start at within the folder (CAPITALIZE IT!).", , 450, 150
if (ErrorLevel)
;braces indicate an expression and the non-legacy if statement
;more about this, as an expression, ErrorLevel here holds the value
;1, which gets evaluated to true, so we're doing
;if (true), which is true
{
MsgBox, % "CANCEL was pressed."
ExitApp
}
else
inputted_letter := UserInput ; = is never used, always :=
Loop, Files, % WhichFolder "\*.*"
;non-legacy file loop
;note that here forcing the expression statement
;with % is actually very much needed
{
current_filename_full := A_LoopFileName
files_first_letter := SubStr(current_filename_full, 1, 1)
if (files_first_letter != inputted_letter)
continue
}
Also you don't have to be concerned about case with !=, it'll always compare case insensitively.

How can I obtain only word without All Punctuation Marks when I read text file?

The text file abc.txt is an arbitrary article that has been scraped from the web. For example, it is as follows:
His name is "Donald" and he likes burger. On December 11, he married.
I want to extract only words in lower case and numbers except for all kinds of periods and quotes in the above article. In the case of the above example:
{his, name, is, Donald, and, he, likes, burger, on, December, 11, he, married}
My code is as follows:
filename = 'abc.txt';
fileID = fopen(filename,'r');
C = textscan(fileID,'%s','delimiter',{',','.',':',';','"','''});
fclose(fileID);
Cstr = C{:};
Cstr = Cstr(~cellfun('isempty',Cstr));
Is there any simple code to extract only alphabet words and numbers except all symbols?
Two steps are necessary as you want to convert certain words to lowercase.
regexprep converts words, which are either at the start of the string or follow a full stop and whitespace, to lower case.
In the regexprep function, we use the following pattern:
(?<=^|\. )([A-Z])
to indicate that:
(?<=^|\. ) We want to assert that before the word of interest either the start of string (^), or (|) a full stop (.) followed by whitespace are found. This type of construct is called a lookbehind.
([A-Z]) This part of the expression matches and captures (stores the match) a upper case letter (A-Z).
The ${lower($0)} component in the regex is called a dynamic expression, and replaces the contents of the captured group (([A-Z])) to lower case. This syntax is specific to the MATLAB language.
You can check the behaviour of the above expression here.
Once the lower case conversions have occurred, regexp finds all occurrences of one or more digits, lower case and upper case letters.
The pattern [a-zA-Z0-9]+ matches lower case letters, upper case letters and digits.
You can check the behavior of this regex here.
text = fileread('abc.txt')
data = {regexp(regexprep(text,'(?<=^|\. )([A-Z])','${lower($0)}'),'[a-zA-Z0-9]+','match')'}
>>data{1}
13×1 cell array
{'his' }
{'name' }
{'is' }
{'Donald' }
{'and' }
{'he' }
{'likes' }
{'burger' }
{'on' }
{'December'}
{'11' }
{'he' }
{'married' }

Issue with eval_in_page - Trying to interpolate an array

my #para_text = $mech->xpath('/html/body/form/table/tbody/tr[2]/td[3]/table/tbody/tr[2]/td/table/tbody/tr[3]/td/div/div/div', type => $mech->xpathResult('STRING_TYPE'));
#BELOW IS JUST TO MAKE SURE THE ABOVE CAPTURED THE CORRECT TEXT
print "Look here: #para_text";
$mech->click_button( id => "lnkHdrreplyall");
$mech->eval_in_page('document.getElementsByName("txtbdy")[0].value = "#para_text"');
In the last line of my code I need to put the contents of the #para_text array as the text to output into a text box on a website however from the "document" till the end of the line it needs to be surrounded by ' ' to work. Obviously this doesnt allow interpolation as that would require " " Any ideas on what to do?
To define a string that itself contains double quotes as well as interpolating variable values, you may use the alternative form of the double quote qq/ ... /, where you can choose the delimiter yourself and prevent the double quote " from being special
So you can write
$mech->eval_in_page(qq/document.getElementsByName("txtbdy")[0].value = "#para_text"/)

Removing Unwanted commas from a csv

I'm writing a program in Progress, OpenEdge, ABL, and whatever else it's known as.
I have a CSV file that is delimited by commas. However, there is a "gift message" field, and users enter messages with "commas", so now my program will see additional entries because of those bad commas.
The CSV fields are not in double qoutes so I CAN NOT just use my main method with is
/** this next block of code will remove all unwanted commas from the data. **/
if v-line-cnt > 1 then /** we won't run this against the headers. Otherwise thhey will get deleted **/
assign
v-data = replace(v-data,'","',"\t") /** Here is a special technique to replace the comma delim wiht a tab **/
v-data = replace(v-data,','," ") /** now that we removed the comma delim above, we can remove all nuisance commas **/
v-data = replace(v-data,"\t",'","'). /** all nuisance commas are gone, we turn the tabs back to commas. **/
Any advice?
edit:
From Progress, I cal call Linux commands. So I should be able to execute C++/PHP/Shell etc all from my Progress Program. I look forward to advice, until then I shall look into using external scripts.
You are not providing quite enough data for a perfect answer but given what you say I think the IMPORT statement should handle this automatically.
In my example here commaimport.csv is a comma-separated csv-file with quotes around text fields. Integers, logical variables etc have no quotes. The last field contains a comma in one line:
commaimport.csv
=======================
"Id1", 123, NO, "This is a message"
"Id2", 124, YES, "This is a another message, with a comma"
"Id3", 323, NO, "This is a another message without a comma"
To import this file I define a temp-table matching the file layout and use the IMPORT statement with comma as delimiter:
DEFINE TEMP-TABLE ttImport NO-UNDO
FIELD field1 AS CHARACTER FORMAT "xxx"
FIELD field2 AS INTEGER FORMAT "zz9"
FIELD field3 AS LOGICAL
FIELD field4 AS CHARACTER FORMAT "x(50)".
INPUT FROM VALUE("c:\temp\commaimport.csv").
REPEAT :
CREATE ttImport.
IMPORT DELIMITER "," ttImport.
END.
INPUT CLOSE.
FOR EACH ttImport:
DISPLAY ttImport.
END.
You don't have to import into a temp-table. You could import into variables instead.
DEFINE VARIABLE c AS CHARACTER NO-UNDO FORMAT "xxx".
DEFINE VARIABLE i AS INTEGER NO-UNDO FORMAT "zz9".
DEFINE VARIABLE l AS LOGICAL NO-UNDO.
DEFINE VARIABLE d AS CHARACTER NO-UNDO FORMAT "x(50)".
INPUT FROM VALUE("c:\temp\commaimport.csv").
REPEAT :
IMPORT DELIMITER "," c i l d.
DISP c i l d.
END.
INPUT CLOSE.
This will render basically the same output:
You don't show what your data file looks like. But if the problematic field is the last one, and there are no quotes, then your best bet is probably to read it using INPUT UNFORMATTED to get it a line at a time, and then split the line into fields using ENTRY(). That way you can treat everything after the nth comma as a single field no matter how many commas the line has.
For example, say your input file has three columns like this:
boris,14.23,12 the avenue
mark,32.10,flat 1, the grange
percy,1.00,Bleak house, Dartmouth
... so that column three is an address which might contain a comma and is not enclosed in quotes so that IMPORT DELIMITER can't help you.
Something like this would work in that case:
/* ...skipping a lot of definitions here ... */
input from "datafile.csv".
repeat:
import unformatted v-line.
create tt-thing.
assign tt-thing.name = entry(1, v-line, ',')
tt-thing.price = entry(2, v-line, ',')
tt-thing.address = entry(3, v-line, ',').
do v=i = 4 to num-entries(v-line, ','):
tt-thing.address = tt-thing.address
+ ','
+ entry(v-i, v-line, ',').
end.
end.
input close.