VBScript checks all OR conditions? - if-statement

Perhaps I'm missing something, but it annoys me that VBScript seems to read all OR condtions. For example, I'd like to do something like this:
If (oFS.FileExists(sFileLoc) = False) Or (sNewText <> oFS.OpenTextFile(sFileLoc).ReadAll) Then
Now I get an error that the file doesn't exist because of the second condition. I was hoping that if the file doesn't exist VBScript would skip immediately to the result, and if it does, it checks the second condition.
Am I right and is this normal behavior?

As M. Harris already said in 2003 and the docs for the logical operators (e.g. Or) state explicitly, VBScript does not short-circuit the evaluation of conditionals. You must use nested Ifs or a slightly fancy Select Case

You can use inline nested IF's to achieve short-circuiting in VBScript. For example, you could rewrite your statement like this:
If oFS.FileExists(sFileLoc) Then If sNewText = oFS.OpenTextFile(sFileLoc).ReadAll Then
But your Then condition must be specified on the same line as this statement. So if you need to perform multiple operations as a result of this condition, you must separate the statements with a colon (:), which is the single-line statement separator in VBScript.
If oFS.FileExists(sFileLoc) Then If sNewText = oFS.OpenTextFile(sFileLoc).ReadAll Then x = 1 : y = 2
You could also just move your logic into a Sub or Function and make a call instead:
If oFS.FileExists(sFileLoc) Then If sNewText = oFS.OpenTextFile(sFileLoc).ReadAll Then DoStuff
Note, too, that if you need to specify an Else clause, it must be specified on this line as well.
If oFS.FileExists(sFileLoc) Then If sNewText = oFS.OpenTextFile(sFileLoc).ReadAll Then x = 1 Else x = 2

Related

How can I allow my program to continue when a regex doesn't match?

I want to use the regex crate and capture numbers from a string.
let input = "abcd123efg";
let re = Regex::new(r"([0-9]+)").unwrap();
let cap = re.captures(e).unwrap().get(1).unwrap().as_str();
println!("{}", cap);
It worked if numbers exist in input, but if numbers don't exist in input I get the following error:
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value'
I want my program continue if the regex doesn't match. How can I handle this error?
You probably want to (re-)read the chapter on "Error Handling" in the Rust book. Error handling in Rust is mostly done via the types Result<T, E> and Option<T>, both representing an optional value of type T with Result<T, E> carrying additional information about the absence of the main value.
You are calling unwrap() on each Option or Result you encounter. unwrap() is a method saying: "if there is no value of type T, let the program explode (panic)". You only want to call unwrap() if an absence of a value is not expected and thus would be a bug! (NB: actually, the unwrap() in your second line is a perfectly reasonable use!)
But you use unwrap() incorrectly twice: on the result of captures() and on the result of get(1). Let's tackle captures() first; it returns an Option<_> and the docs say:
If no match is found, then None is returned.
In most cases, the input string not matching the regex is to be expected, thus we should deal with it. We could either just match the Option (the standard way to deal with those possible errors, see the Rust book chapter) or we could use Regex::is_match() before, to check if the string matches.
Next up: get(1). Again, the docs tell us:
Returns the match associated with the capture group at index i. If i does not correspond to a capture group, or if the capture group did not participate in the match, then None is returned.
But this time, we don't have to deal with that. Why? Our regex (([0-9]+)) is constant and we know that the capture group exists and encloses the whole regex. Thus we can rule out both possible situations that would lead to a None. This means we can unwrap(), because we don't expect the absence of a value.
The resulting code could look like this:
let input = "abcd123efg";
let re = Regex::new(r"([0-9]+)").unwrap();
match re.captures(e) {
Some(caps) => {
let cap = caps.get(1).unwrap().as_str();
println!("{}", cap);
}
None => {
// The regex did not match. Deal with it here!
}
}
You can either check with is_match or just use the return type of captures(e) to check it (it's an Option<Captures<'t>>) instead of unwrapping it, by using a match (see this how to handle options).

Postgres set varchar field to regular expression of itself

I'm trying to normalise a data field by removing a fairly common postfix. I've got as far as using the substring() function in postgres, but can't quite get it to work. For example, if I want to strip the postfix 'xyz' from any values that have it;
UPDATE my_table SET my_field=substring(my_field from '#"%#"xyz' for '#');
But this is having some weird effects that I cant pin down. Any thoughts? Many thanks as always.
update my_table
set my_field = regexp_replace(my_field, 'xyz$', '')
where my_field ~ 'xyz$';
This will also change the value 'xyz' into an empty string. I don't know if you want that (or if the suffix can exists "on it's own".
The where clause is not strictly necessary but will make the update more efficient because only those rows are updated that actually meet the criteria.
UPDATE my_table
SET my_field = left(my_field, -3)
WHERE my_field LIKE '%xyz';
For several reasons:
If you don't want to change every single row, always add a WHERE clause to your UPDATE. Even if only some rows are actually changed by the expression. An UPDATE from the same value to the same value is still an UPDATE and will produce dead rows and table bloat and trigger triggers ...
Use left() in combination with LIKE.
left() with a negative second parameter effectively trims the number of character from the end of the string. left() was introduced with PostgreSQL 9.1. I quote the manual here:
When n is negative, return all but last |n| characters.
Always pick LIKE over a regular expression (~) if you can. LIKE is not as versatile, but much faster. (SIMILAR TO is rewritten as regular expression internally). Details in this related answer on dba.SE.
If you want to make sure that a minimum of characters remains:
WHERE my_field LIKE '_%xyz'; -- prepend as many _ as you want chars left
substring() would work like this (one possibility):
substring(my_field, '^(.*)xyz$');

Regular expressions for phone number patterns

Do you know if is possible to transform a pattern like this to regular expressions:
ABCDXXXXYYYY
Where ABCDEFGH.. are consecutive numbers and V, X, Y, Z are any number.
The pattern above should match:
123400006666
456799994444
etc.
Please note that I'm not asking for a full solution, but some idea on how to approach this problem. Have you ever faced a situation like this before (to search a DB for defined patterns that doesn't seem to fit RegExps?
Any comment would be really appreciated.
You can't identify consecutive numbers in a regular expression as they're too context dependant.
However, I think this would be easily possible in PL/SQL and possibly possible in SQL.
If you only want to use SQL then you can generate a string of consecutive numbers using a combination of connect by and either the undocumented function wm_contact or the user-defined function stragg
Something like:
select replace(stragg(level),',','')
from dual
connect by level <= 5
Concatenating this with a regular expression may get you close but I don't think that this is the way to go. I would definitely investigate using a PL/SQL function and possibly forgetting about regular expressions completely.
Doing the following will split out a number into an array, which you can then loop through and manipulate. As requested, this is just a starting point and you may want to change it around a lot. As there's no actual SQL and it's just string manipulation it's pretty efficient doing something like this.
create or replace function validate_phone( P_phone number )
return number is
type t__phone is table of number index by binary_integer;
t_phone t__phone;
l_consecutive varchar2(150);
begin
-- Test whether we actually have a number first ( code below ).
if is_number(P_phone) = 0 then
return null;
end if;
-- Split out the phone number into individual array elements.
for i in 1 .. length(to_char(P_phone)) loop
t_phone(i) := substr(to_char(P_phone, i, 1))
end loop;
for i in t_phone.first .. t_phone.last loop
-- If we find a consecutive number then build this string.
if t_phone.exists(i + 1)
and t_phone(i) = t_phone(i + 1) - 1 then
l_consecutive := l_consecutive || t_phone(i);
end if;
end loop;
return something;
end validate_phone;
You may, as indicated in the above want to check whether your phone number is actually numeric first:
create or replace function is_number( P_number varchar2 )
return number is
/* Test a number to see whether it actually is one
return a 1 / 0 rather than boolean so it can also
be used in plain SQL.
*/
l_number number;
begin
l_number := P_number;
return 1;
exception when others then
return 0;
end is_number;
The language you describe is not context-free (in case the length of the prefix consisting of consecutive numbers is arbitrary) and not a regular language, therefore can not be expressed by a regular expression.

regex and file read line in autohotley

well i am currently writing a script that is meant to check the logs of another script i wrote to see if it has had three or more unsuccessful pings in a row before a successful one, this is just barebones at the moment but it should look something like this
fileread,x,C:\Users\Michael\Desktop\ping.txt
result:=RegExMatch(%x% ,failure success)
msgbox,,, The file is = %x% `n the result is = %result%
now the file that is trying to read is
success failure success
and for some reason, when it reads the file it says that the variable %x% 'contains illegal characters
when i copy and paste the contents of ping.txt into the script and save it as a variable it works
i have made sure that the file has windows line endings CR +LF
i have assigned the variable generated in file read as another variable thus stripping any trailing or leading whitespace characters
the file is encoded in ANSI and still has the problem with UTF8
Function parameters take variable names without the % symbol, simply remove them.
I also want to point out that if the second parameter is meant to be a regular expression,
instead of a variable containing a regular expression, you will need quotes around it.
As is your script passes an empty string as the pattern which will always return 1
(failure is interpreted as a variable with an empty string associated with it.).
To quote Lexikos:
"An empty string, when compiled as a regex pattern, will match exactly
zero characters at whatever position you attempt to match it. Think of
it this way: For any position n in any string, the next 0 characters
are always the same."
Because you are simply truth testing,
or finding the index I want to point out that Autohotkey has a useful shorthand operator for this.
string := "this is a test"
f1::
result := RegExMatch(string, "\sis")
traytip,, %result%
Return
f2::
result := string ~= "\sis"
traytip,, % result
Return
These hotkeys both do the same thing; the second uses the shorthand operator ~=
and notice how the traytip parameter in the second example has only one %
When you start a command parameter with a % that starts an expression,
and within an expression variables are not enclosed with %.
The ternary operator ?: is also very useful:
string := "this is a test"
f3::traytip,, % (result := string ~= "\sis") ? (result) : ("nothing")
It might look complicated but it's very simple.
Think of
% as if
? as then
: as else
If (true) then (a) else (b)
% (true) ? (a) : (b)
A variable will be evaluated as False if 0 (or nothing) is assigned to it.
But in this example "\sis" is matched and the index of the space is returned (5),
so it is evaluated as True.
You can read more about variables and operators here:
http://l.autohotkey.net/docs/Variables.htm

How to process this string via regular expression

my string style like this
expression1/field1+expression2*expression3+expression4/field2*expression5*expression6/field3
a real style mybe like this:
computer/(100)+web*mail+explorer/(200)*bbs*solution/(300)
"+" and "*" represent operator
"computer","web"...represent expression
(100),(200) represent field num . field num may not exist.
I want process the string to this:
<computer>/(100)+web*<mail>+explorer/(200)*bbs*<solution>/(300)
rules like this
if expression length is more than 3 and its field is not (200), then add brackets to it.
My recommendation is to mix regex with other language features. The complication arises from the fact that field appears before expressions, and lookbehind is usually more limited than lookforward.
In pseudo-Java-code, I recommend doing something like this:
String[] parts = input.split("/");
for (int i = 0; i < parts.length; i++) {
if (!parts[i].startsWith("(200)"))
parts[i] = parts[i].replaceAll("(?=[a-z]{4})([a-z]+)", "<$1>");
}
String output = parts.join("/");
I would not use just regular expression.
You say "if expression length is more than 3 and its field is not (200), then add brackets to it"
I think a normal conditional statement is the best and clearest solutoion for this.
I think regular expressions are sometimes overused. Regexes are hard to read, and when a couple of conditional statements can do the same but more clearly, then I'd say the code quality is higher.