Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

StringMod() String Manipulation - Enhanced by PhiLho / Titan


  • Please log in to reply
77 replies to this topic
SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
StringMod() was originally posted in General Chat topic Numerology .

[*:2urvzjq0]PhiLho enhanced the original code & included Replace, Rot13 and Rot47
[*:2urvzjq0]Titan re-wrote the function and enhanced it with Scramble, Bloat, Pattern.


Function StringMod() extracts, excludes or replaces specified characters from the String passed on to it and returns
the modified string. It can also flip a string (reversed order) or convert it into Rot13 and Rot47.
Additional functionality are detailed below.

Usage: StringMod(string, method [, param1, param2])

String Modification: Methods with examples

Methods:

Only
Omit
Flip
Replace
Rot13
Rot47
Scramble
Bloat
Pattern
[/list]Examples:

Extract the Vowels from the name "Goyyah"
StringMod("Goyyah","Only","AEIOU") returns "oa"

Extract the Consonants from the name "Goyyah"
StringMod("Goyyah","Omit","AEIOU") returns "Gyyh"

Flips "Goyyah"
StringMod("Goyyah","Flip") returns "hayyoG" [*:2urvzjq0]Credit: Thanks to Rubberduck for his function StringFlip()[/list]Replace "*^" in "^H*e*l*l*o^" with "_"
StringMod("^H*e*l*l*o^", "Replace" , "*^" , "_") returns "_H_e_l_l_o_"

Example for Validating a String into Filename

filename = This?Is?\A:/<"Valid"File>*Name?.txt 
invalidFilenameCharacters=\/:*?"<>|
Msgbox, % StringMod(filename, "Replace", invalidFilenameCharacters) 
; This_Is_A___Valid_File__Name_.txt

Convert "HELLO" into Rot13
StringMod("Hello", "Rot13") returns "UYRRB"

Convert "HELLO" into Rot47
StringMod("Hello", "Rot47") returns "wt{{~"

Scramble the word "AUTOHOTKEY"
StringMod("AUTOHOTKEY","Scramble") will return something like "UKOTTOEAHY"

[*:2urvzjq0]Standalone version of Scramble()[*:2urvzjq0] Laszlo's CharPerm() - similar functionality - optimised
Stuff Characters before and after each character in the word "Autohotkey"
StringMod("AutoHotkey","Bloat", "{" , "}" ) returns "{A}{u}{t}{o}{H}{o}{t}{k}{e}{y}"

[*:2urvzjq0]Credit: Thanks to Laszlo for his function CharBloat()
Pattern may be used to find the Anagrammatical relationship between two words.
Pattern returns a String arranged in alphabetical order in such
a way that
the number of occurences follows each letter

Pattern1 := StringMod("RACE","Pattern") ; "A1C1E1R1"
Pattern2 := StringMod("CARE","Pattern") ; "A1C1E1R1"
If Pattern1 = %Pattern2%
; evaluates to a logical true because both Pattern1 and Pattern2 share the same pattern "A1C1E1R1"

[*:2urvzjq0] Laszlo's comments/variant(s) on Pattern is here[*:2urvzjq0]Download a Demo: Anagram / Jumbled word Finder 745KB
[*:2urvzjq0]Visit Brendan's On-Line Anagram Generator [/list] [/list]

Downloads :

[*:2urvzjq0]PhiLho`s enhanced version of StringMod() : Download latest version.

[*:2urvzjq0]Titan`s enhanced version of StringMod() : Download latest version.[*:2urvzjq0]2006-Dec-29: A new RegEx based StringMod() has been posted here[/list][/list]
Posted Image
kWo4Lk1.png

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Other String Manipulation Functions

StringSlice() : Added 2006-March-19.

StringSlice() : Similar to AHK's StringSplit Command - However, with one difference.
String is Split, based on defined width rather than a delimiter

Example: StringSlice("Dictionary", 3, "Var") will initialise 5 Variables: Var0 through Var4

Var0 will contain 4
Var1 will contain "Dic"
Var2 will contain "tio"
Var3 will contain "nar"
Var4 will contain "y"[*:9q0asq70]See Laszlo's Optimised StringSlice()
[*:9q0asq70]See PhiLho's Variant : SliceString()

Regards, :)
kWo4Lk1.png

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
Thanks for the credit :-)
For consistency, I would have integrated Flip into the loop:
Loop Parse, _string
	{
		If (_mode = "Flip")
		{
			rStr := A_LoopField rStr
		}
		; Is char in the given list?
		Else IfInString _chars, %A_LoopField%
		{
			If (_mode = "Only")
				rStr := rStr A_LoopField
			Else If (_mode = "Replace")
				rStr := rStr _replCh
		}
		Else
		{
			If (_mode = "Omit" or _mode = "Replace")
				rStr := rStr A_LoopField
		}
	}
Also I believe that the Flip code you gave may give bad results if the string has spaces (in default mode).
Such Flip function is probably not of huge utility, except perhaps to perform some AHK functions that doesn't provide a facility starting from the right of the string.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
Another transformation: Rot13, Rot47 and variants.
File can be downloaded from AutoHotkey.net.
StringMod(_string, _mode="", _chars="", _replCh="")
{
	local rStr, char, ascii, o

	; Defaults
	If _mode not in Omit,Only,Replace,Flip,Rot13,Rot47
		_mode = Omit
	If _chars =
	{
		If _mode = Rot13
			_chars = 13
		Else If _mode = Rot47
			_chars = 47
		Else
			_chars = %A_Space%
	}
	If _replCh =
		_replCh = _

	Loop Parse, _string
	{
		If (_mode = "Flip")
		{
			rStr := A_LoopField rStr
		}
		Else If (_mode = "Rot13")
		{
			char := Asc(A_LoopField)
			; o is 'A' code if it is an uppercase letter, and 'a' code if it is a lowercase letter
			o := Asc("A") * (Asc("A") <= char && char <= Asc("Z")) + Asc("a") * (Asc("a") <= char && char <= Asc("z"))
			If (o > 0)
			{
				; Set between 0 and 25, add rotation factor, modulus alphabet size
				char := Mod(char - o + _chars, 26)
				; Transform back to char, upper or lower
				char := Chr(char + o)
			}
			Else
			{
				; Non alphabetic, unchanged
				char := A_LoopField
			}
			rStr := rStr char
		}
		Else If (_mode = "Rot47")
		{
			char := A_LoopField
			If char between ! and ~
			{
				char := Asc(A_LoopField)
				; Set between 0 and 93, add rotation factor, modulus range size
				char := Mod(char - 33 + _chars, 94)
				; Transform back to char
				char := Chr(char + 33)
			}
			rStr := rStr char
		}
		; Is char in the given list?
		Else IfInString _chars, %A_LoopField%
		{
			If (_mode = "Only")
				rStr := rStr A_LoopField
			Else If (_mode = "Replace")
				rStr := rStr _replCh
		}
		Else
		{
			If (_mode = "Omit" or _mode = "Replace")
				rStr := rStr A_LoopField
		}
	}
	Return rStr
}

; Run test code only if the file is ran standalone
If (A_ScriptName = "StringMod.ahk")
{

str = The name of the variable whose contents will be analyzed.
filename = This?Is?\A:/<"Valid"File>*Name?.txt
invalidFilenameCharacters=\/:*?"<>|
r = |
r := r StringMod(str, "OMIT", " ") "|`n|"
r := r StringMod(str, "Omit", "aeiouy") "|`n|"
r := r StringMod(str, "only", " ") "|`n|"
r := r StringMod(str, "Only", "aeiouy ") "|`n"
r := r StringMod(str, "RePlAcE", " ") "|`n|"
r := r StringMod(str, "Replace", "aeiouy", "$") "|`n|"
r := r StringMod(str, "Flip") "|`n|"
r := r StringMod(str, "Rot13") "|`n|"
r := r StringMod(str, "Rot13", 11) "|`n|"
r := r StringMod(str, "Rot47") "|`n|"
r := r StringMod(str, "Rot47", 33) "|`n|"
r := r StringMod(filename, "Omit", invalidFilenameCharacters) "|`n|"
r := r StringMod(filename, "Replace", invalidFilenameCharacters) "|`n|"
r := r StringMod(filename, "Replace", invalidFilenameCharacters, " Fun ") "|`n"
MsgBox %r%
a1 := StringMod(str, "Rot13")
b1 := StringMod(a1, "Rot13")
a2 := StringMod(str, "Rot13", 7)
b2 := StringMod(a2, "Rot13", 26-7)
MsgBox Rot13:`n%str%`n%a1%`n%b1%`n%a2%`n%b2%
a1 := StringMod(str, "Rot47")
b1 := StringMod(a1, "Rot47")
a2 := StringMod(str, "Rot47", 13)
b2 := StringMod(a2, "Rot47", 94-13)
MsgBox Rot47:`n%str%`n%a1%`n%b1%`n%a2%`n%b2%

}

Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Nice functions!
…you could save a few lines at setting "o", with Shimanov's trick
o := 65*(64<char && char<91)+97*(96<char && char<123)


PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
Thanks!
I like the If char between 65 and 87 as it was quite readable.
But I like the logic behind the expression, and using Asc calls, it is self-documenting (more or less!). And since this function is growing, saving some lines isn't too bad.
It is perhaps a bit slower, calling Asc 6 times on each loop, but I doubt it is noticeable, we rarely Rot13 whole books...
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Dear PhiLho, :)

Rot13, Rot47 and variants: :shock: I admit that I never knew about it, Thanks for Info + Function


For consistency, I would have integrated Flip into the loop:


I have in my mind of adding more functionality to StringMod() like

StringMod(String,"Upper")
StringMod(String,"Lower")
StringMod(String,"Title")
or StringMod(String,"Proper")


which does not require the string parsing loop & so I thought FLIP may stay out of the parsing loop.

More functionality can be added, like

StringMod(String,"OnlyDigits") ; _chars should be assigned 0123456789
StringMod(String,"A2Z") ; _chars should be assigned ABCDEFGHIJKLMNOPQRSTUVWXYZ

Instead of the present way

StringMod(String,"Only","0123456789")
StringMod(String,"Only","ABCDEFGHIJKLMNOPQRSTUVWXYZ"


Also, I am thinking about consolidating independent AHK string commands into a few but powerful functions like
writing one single function StringGet() for
StringGet(String,"Right",1)
StringGet(String,"Left",1)
StringGet(String,"Middle",10,1)

etc.

Flip function is probably not of huge utility, except perhaps to perform some AHK functions that doesn't provide a facility starting from the right of the string


Yes! I agree Flip is a fancy option, but maybe it will be useful for those writing word based games in AHK ( like me :wink:).

Regards, :)
Note: If anybody interested in Anagram/Jumbled word finder let me know. I've written a function for it
kWo4Lk1.png

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

StringGet(String,"Right",1)
StringGet(String,"Left",1)
StringGet(String,"Middle",10,1)

You could even save the second parameter. Specify the start and end character index, where a negative index should mean counting from the end. If you call it with 2 parameters, StringGet(String,3) = StringGet(String,1,3), that is, "3 chars from Left"; or StringGet(String,-2) = StringGet(String,-2,-1), that is, "2 chars from Right". I used these in the list manipulation library.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Dear Laszlo, :)

Offtopic: I just visited List manipulation functions. I am yet to get comfortable with Listview but your functions will be of a great help to me!

You could even save the second parameter. Specify the start and end character index, where a negative index should mean counting from the end.If you call it with 2 parameters,
StringGet(String,3) = StringGet(String,1,3), that is, "3 chars from Left"; or
StringGet(String,-2) = StringGet(String,-2,-1), that is, "2 chars from Right".
I used these in the list manipulation library.


Thats a good idea!. What do you suggest for StringMid()

Regards, :)
kWo4Lk1.png

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
Nice funciton :)
Here is my improved rewrite - it works slightly faster, is smaller in code size and supports character rotation of n, Flip, Omit, Replace and Only (the first few lines is for demonstration):
str = The name of the variable whose contents will be analyzed.
r := "Flip:`t" . StringMod(str, "Flip") . "`nOmit "" "":`t"
	. StringMod(str, "Omit", " ")	. "`nRot13:`t" . StringMod(str, "Rot13")
	. "`nRot47:`t" . StringMod(str, "Rot47") . "`nOnly ..:`t" . StringMod(str, "only", "aeiou")
MsgBox, 64, StringMod(), Original:`t%str%`n`n%r%

StringMod(s, m="", ch="", rc="") { ; enhanced by Titan :)
	xAT := A_AutoTrim
	AutoTrim, Off
	IfEqual, ch, , SetEnv, ch, %A_Space%
	IfEqual, rc, , SetEnv, rc, _
	Loop Parse, s
		If InStr(m, "Rot") {
			StringReplace, n, m, Rot
			l := Asc(A_LoopField)
			Loop, 2 {
				If (A_Index = 1) {
					f = 65
					c = 90
				} Else {
					f = 97
					c = 122
				} If (l >= f and l <= c) {
					l += n
					Loop
						If (l > c)
							l := l - c + f - 1
						Else
							Break
				} }
				e := e . Chr(l)
		} Else If m =Flip
			e =%A_LoopField%%e%
		Else
			If InStr(ch, A_LoopField) {
				If m =Only
					e =%e%%A_LoopField%
				Else If m =Replace
					e = %e%%rc%
			} Else If m !=Only
					e =%e%%A_LoopField%
	Autotrim, % xAT
	Return, e
}

Edit: updated rotation algorithm

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Dear Titan
Posted Image
Great work!
kWo4Lk1.png

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Every String in my Original Code has been modified :?: :!: except for the function name StringMod()

Regards :D :D :D
kWo4Lk1.png

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

Every String in my Original Code has been modified :?: :!: except for the function name StringMod()

Yep, it was a complete rewrite :) thanks for the credit.
Btw. that smilie icon is awesome :D

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Dear Titan,

StringMod("HELLO","Rot13")

should return "URYYB"

whereas

it returns "URYY\"

Please clarify..

Also, I want to put a link for a downloadable .AHK in the first post,
so please host it and provide me a link to the same.

Regards, :)

Edit: Titan updated his code
kWo4Lk1.png

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
Well, Titan's code is short and al, but it doesn't do true Rot13, which acts only on alphabetic characters and replaces them only by alphabetic chars...
So it won't decode Rot13-encoded secret messages given on Usenet (or elsewhere).
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")