I've written a script so I can quickly get a link to files and folders on my server. The problem is that the string I get does not escape spaces.
Can someone tell me how to escape the spaces for '%20' ?
tell application "Finder"
set sel to (the POSIX path of (the selection as alias))
set sel to ((characters 10 thru -1 of sel) as string)
set sel to "afp://myserver._afpovertcp._tcp.local/" & sel
set the clipboard to sel
end tell
Please help thanks!
You can use the text item delimiters to find and replace characters
tell application "Finder" to set sel to POSIX path of (the selection as alias)
set the clipboard to "afp://myserver._afpovertcp._tcp.local/" & (my findReplace(text 10 thru -1 of sel, " ", "%20"))
on findReplace(t, toFind, toReplace)
set {tid, text item delimiters} to {text item delimiters, toFind}
set t to text items of t
set text item delimiters to toReplace
set t to t as text
set text item delimiters to tid
return t
end findReplace
Following code includes 3 encoding routines from
https://macosxautomation.com/applescript/sbrt/sbrt-08.html
A standard practice when creating URL's is to
encode special characters (high-level ASCII) and spaces to their
hexidecimal equivalents. For example, spaces in URL's are converted
to: %20
Script:
tell application "Finder"
set sel to (the POSIX path of (the selection as alias))
set sel to ((characters 10 thru -1 of sel) as string)
set sel to "afp://myserver._afpovertcp._tcp.local/" & my encode_filepath(sel, true, false)
set the clipboard to sel
end tell
-- this sub-routine encodes special characters in a filepath:
-- My Disk:My Folder:My File.htm
-- My%20Disk:My%20Folder:My%20File.htm
on encode_filepath(this_file, encode_URL_A, encode_URL_B)
set this_file to this_file as text
set AppleScript's text item delimiters to ":"
set the path_segments to every text item of this_file
repeat with i from 1 to the count of the path_segments
set this_segment to item i of the path_segments
set item i of the path_segments to my encode_text(this_segment, encode_URL_A, encode_URL_B)
end repeat
set this_file to the path_segments as string
set AppleScript's text item delimiters to ""
return this_file
end encode_filepath
-- TEXT ENCODING: encode spaces and high-level ASCII characters (those above 127)
-- encode_URL_A = encode most of the special characters reserved for use by URLs.
on encode_text(this_text, encode_URL_A, encode_URL_B)
set the standard_characters to "abcdefghijklmnopqrstuvwxyz0123456789"
set the URL_A_chars to "$+!'/?;&#=#%><{}[]\"~`^\\|*"
set the URL_B_chars to ".-_:"
set the acceptable_characters to the standard_characters
if encode_URL_A is false then set the acceptable_characters to the acceptable_characters & the URL_A_chars
if encode_URL_B is false then set the acceptable_characters to the acceptable_characters & the URL_B_chars
set the encoded_text to ""
repeat with this_char in this_text
if this_char is in the acceptable_characters then
set the encoded_text to (the encoded_text & this_char)
else
set the encoded_text to (the encoded_text & encode_char(this_char)) as string
end if
end repeat
return the encoded_text
end encode_text
-- encoding high-ASCII characters:
on encode_char(this_char)
set the ASCII_num to (the ASCII number this_char)
set the hex_list to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}
set x to item ((ASCII_num div 16) + 1) of the hex_list
set y to item ((ASCII_num mod 16) + 1) of the hex_list
return ("%" & x & y) as string
end encode_char
Related
I am tryin to convert a table I have with a limit of 16 characters to hex keys and then store those hex keys in another table in the same order as the original string.
I have found a function using some online help which can take a string, split it into each character and assign that character to a space in a table.
So if I write function("Hello World!")
the return of the function would be table= {H, e, l, l, o, , W, o, r, l, d, !)
Now I need to limit this to only accept 16 characters and any more than that are discarded.
The function goes as
local str = "Hello World"
local chars = {}
for c in string.gmatch(str, ".") do
chars[#chars+1] = c
end
How can I limit this to 16 elements in the table?
My second question would be after I create this table, how do I go about changing each element of this table to a hex key and storing it in another table in the same order as the original string?
EDIT1: Thank you luke10000 for suggesting the if char.. method, it works perfectly. Now the next part is to convert this to hex and place it in a table.
I found a function which does this operation but there was no explanation with it at all, I understand some of it but not all. I can only print with this to the console all the hex keys.
function hex_dump(buf)
for byte=1, #buf, 16 do
local chunk = buf:sub(byte, byte+15)
--io.write(string.format('%08X ',byte-1)) -- 0's
chunk:gsub('.', function (c) io.write(string.format('0x%X ',string.byte(c))) end)
io.write(string.rep(' ',3*(16-#chunk)),"\n")
--io.write(' ',chunk:gsub('%c','.'),"\n") -- add the character infront of hex key
end end
for _, chars in ipairs(chars) do
hex_dump(chars) end
I'm not sure how the rest of your post is related to your actual problem.
I am tryin to convert a table I have with a limit of 16 characters to
hex keys and then store those hex keys in another table in the same
order as the original string.
I'm also not sure what you mean with "hex key".
local input = {"H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"}
local output = {}
for i,v in ipairs(input) do
output[i] = string.format("0x%X", v:byte())
if i == 16 then break end
end
print(table.concat(output, ", "))
Regarding your other questions:
local str = "Hello World"
local chars = {}
for c in string.gmatch(str, ".") do
chars[#chars+1] = c
end
How can I limit this to 16 elements in the table?
Simply limit your input string to 16 characters.
for c in str:sub(1,16):gmatch(".") do ...
My second question would be after I create this table, how do I go
about changing each element of this table to a hex key and storing it
in another table in the same order as the original string?
If your actual problem is to get a table of number representations from a string, simply use string.byte
local str = "Hello world! This is a wonderful day.!"
local output = table.pack(str:byte(1, 16))
My task is to generate a random string with following parameters:
At least one Uppercase
At least one lower
At least one digit
No repeated chars/digits allowed ( e.g. aa not allowed, aba is allowed, Aa is allowed)
I'm able to generate a random string with 1,2,3 parameters but parameter 4 logic is missing.
inputChars = [('a'..'z'), ('A'..'Z'),(0..9)].map(&:to_a).flatten
string = (0...16).map { inputChars[rand(inputChars.length)] }.join
require 'set'
inputChars = [('a'..'z'), ('A'..'Z'),(0..9)].map(&:to_a).flatten
set_string = Set.new
loop do
break if set_string.size == 16
cr = inputChars[rand(inputChars.length)]
set_string << cr
end
output = set_string.to_a.join
i just change your map operation to loop operation and add Set data structure to store the character from random inputChars operation. Using Set will not allow same character
Let's begin by defining two constants.
CHARS_BY_TYPE = {
lower: ('a'..'z').to_a.freeze,
upper: ('A'..'Z').to_a.freeze,
digit: ('0'..'9').to_a.freeze
}.freeze
ALL = (CHARS_BY_TYPE[:lower] + CHARS_BY_TYPE[:upper] + CHARS_BY_TYPE[:digit]).freeze
#=> [["a", "b",..., "z", "A", "B",..., "Z", "0", "1",..., "9"]
I will initially build a string of a specified length by randomly selecting one character at a time from the array ALL, ensuring that no two consecutive characters are the same. There is no assurance, however, that the resulting string will contain at least one uppercase letter, one lowercase letter and one digit.
def append_random_char(last_char)
loop do
ch = ALL.sample
break ch unless ch == last_char
end
end
Our main method will begin as follows:
def random_string(str_len)
raise ArgumentError if str_len < 3
(str_len - 2).times.with_object('') { |_,s| s << append_random_char(s[-1]) }
# ...
end
For example:
s = random_string(40)
#=> "arN64kDw6ClzcNMj8WAkj1NJC2B5oFoRlcXl5S"
str_len is the required string length, 40 in the example. Observe that s contains 38 characters of which no two successive characters are equal. We will need to add 2 characters later. If the string contained no digits, for example, at least one of those two characters added (at a random location) will be a (randomly-selected) digit. If the string were shorter and contained, for example, digits only, the two characters added will be an uppercase letter and a lowercase letter.
Next we need to see if the string is lacking an uppercase letter, a lowercase letter and/or a digit. (It cannot be missing all three, as the string must contain at least three characters.)
require 'set'
def types_to_add(str)
[:lower, :upper, :digit].select do |type|
st = CHARS_BY_TYPE[type].to_set
str.each_char.none? { |ch| st.include?(ch) }
end
end
For the random string generated above we obtain:
types_to_add(s)
#=> []
meaning that the string contains at least one uppercase letter, one lowercase letter and one digit. Try this:
types_to_add(s.gsub(/\d|[A-Z]/, '')
#=> [:upper, :digit]
See Enumerable#none?. CHARS_BY_TYPE[type] is converted to a set merely to speed look-ups.
Suppose now we need to insert an uppercase letter, lowercase letter or digit to satisfy the requirement that there is at least one of each in the string. Specifically, we wish to insert a randomly-drawn character (from CHARS_BY_TYPE[:lower], CHARS_BY_TYPE[:upper] or CHARS_BY_TYPE[:digit]) at a random location in the string we are constructing, with the restriction that neither the preceding nor following character is the same character.
def insert_in_string(str, ch)
i = loop do
i = rand(str.size + 1)
next if ch == str[i]
break i if i.zero? || ch != str[i-1]
end
str.insert(i, ch)
end
For example, if we were to insert the character '0' into (a copy of) our string s (which is not needed):
insert_in_string(s.dup, '0')
#=> "arN64kDw6ClzcN0Mj8WAkj1NJC2B5oFoRlcXl5S"
s #=> "arN64kDw6ClzcNMj8WAkj1NJC2B5oFoRlcXl5S"
^
This inserts the character ch before the character in str at index i. If rand(str.size + 1) returns str.size ch is inserted after the last character of str.
Following this operation the final step is to use the method append_random_char to build the string out to the desired length.
The completed main method is as follows.
def random_string(str_len)
raise ArgumentError if str_len < 3
s = (str_len - 2).times.with_object('') { |_,s| s << append_random_char(s[-1]) }
types_to_add(s).each { |type| insert_in_string(s, CHARS_BY_TYPE[type].sample) }
(str_len - s.size).times { s << append_random_char(s[-1]) }
s
end
s = random_string(40)
#=> "PtQrVFZWUYFwiwRy3ySfAy42G1NT98J6cMVMaWeT"
s.match?(/[a-z]/)
#=> true
s.match?(/[A-Z]/)
#=> true
s.match?(/\d/)
#=> true
s.size
#=> 40
This is how I would do it (warning: Not tested. Just want to present the idea
for my algorithm). I first take a random number for the length of the resulting random string (the length will be between 4 and 16 characters). Then I determine
randomly, how many of them are upper case / lower case / digits, and based on
these decision, I generate the string, ensuring that I don't get any duplicates
in succession.
uchars=('A'..'Z').to_a
lchars=('a'..'z').to_a
dchars=('0'..'9').to_a
charmap = { u: uchars, l: lchars, d: dchars }
total_length=rand(13)+4 # Total length of string to be generated
total_u=rand(total_length-3)+1 # Total number of uchars to be generated
total_l=rand(total_length-total_u-2)+1 # Total number of lchars
total_d=total_length-total_u-total_l # Total number of digits
# Array of types to generate
chartypes=([:u]*total_u + [:l]*total_l + [:d]*total_d).shuffle
# chartypes is an array similar to [:u,:d,:d,:l,:u], where the
# symbols designate the kind of character to be generated.
# outstr : random string to be generated
outstr = charmap[chartypes.first].sample
last_char = outstr.dup
total_length.times do |index|
loop do
nextchar = charmap[chartypes[index]].sample
if nextchar != last_char
outstr << nextchar
last_char = nextchar
break
end
end
end
I have a word-filter that searches for words using this regex:
/\b[a-zA-Z-]+\b/
Searching the word: "hello".
Case 1: "Hello there" = true
Case 2: "Hello0 there" = false
Case 3: "Hello_there" = false
Case 4: "Hello-there" = false
Case 5: "4Hello there" = false
How can I setup the Word Boundaries to also find words that start/end with a number, underscore, hyphen or any other character other than a letter?
You may use
/(?<![^\W\d])[a-zA-Z]+(?![^\W\d])/
The (?<![^\W\d]) negative lookbehind matches a location that is not immediately preceded with a char other than a non-word and a digit char, i.e. there must be either start of string or a word char but a digit.
The (?![^\W\d]) negative lookahead matches a location that is not immediately followed with a char other than a non-word and a digit char, i.e. there must be either end of string or a word char other than a digit.
With Ruby, how do I replace a range of characters in a string? For instance, given teh string
hellothere
If I want to replace characters at index positions two through five inclusive with "#" to result in a string
he####here
How would I do this?
You could get a string range and replace it by setting the new character multiplied for the last index plus 1 less the first index:
def replace_in_string(str, replace, start, finish)
str[start..finish] = replace * (finish + 1 - start)
str
end
p replace_in_string 'hellothere', '#', 2, 5
# "he####here"
I'm able to capitalize the first letter of my string using:
str:gsub("^%l", string.upper)
How can I modify this to capitalize the first letter of every word in the string?
I wasn't able to find any fancy way to do it.
str = "here you have a long list of words"
str = str:gsub("(%l)(%w*)", function(a,b) return string.upper(a)..b end)
print(str)
This code output is Here You Have A Long List Of Words. %w* could be changed to %w+ to not replace words of one letter.
Fancier solution:
str = string.gsub(" "..str, "%W%l", string.upper):sub(2)
It's impossible to make a real single-regex replace because lua's pattern system is simple.
in the alternative answer listed you get inconsistent results with words containing apostrophes:
str = string.gsub(" "..str, "%W%l", string.upper):sub(2)
will capitalize the first letter after each apostrophe irregardless if its the first letter in the word
eg: "here's a long list of words" outputs "Here'S A Long List Of Words"
to fix this i found a clever solution here
utilizing this code:
function titleCase( first, rest )
return first:upper()..rest:lower()
end
string.gsub(str, "(%a)([%w_']*)", titleCase)
will fix any issues caused by that weird bug
function titleCase( first, rest )
return first:upper()..rest:lower()
end
string.gsub(str, "(%a)([%w_']*)", titleCase)
BunchOfText {"Yeppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp"}
I have a feeling I will be returning to this question when I need to put something in proper title case.
Below is the Lua code to do exactly that.
It has the disadvantage of not preserving the original spacing between words but it's good enough for now.
-- Lua is like python in syntax, and barebones like C -_-
function Set (list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end
function firstToUpper(str)
return (str:gsub("^%l", string.upper))
end
function titlecase(str)
-- We need to break the string into pieces
words = {}
for word in string.gmatch(str, '([^%s]+)') do
table.insert(words, word)
end
-- We need to capitalize anything that is not a:
-- - Article
-- - Coordinating Conjunction
-- - Preposition
-- Thus we have a blacklist of such words
local blacklist = Set {
"at", "but", "by", "down", "for", "from",
"in", "into", "like", "near", "of", "off",
"on", "onto", "out", "over", "past", "plus",
"to", "up", "upon", "with", "nor", "yet",
"so", "the"
}
for index, word in pairs(words) do
if(not (blacklist[word] ~= nil)) then
words[index] = firstToUpper(word)
end
end
-- First and last words are always capitalized
words[1] = firstToUpper(words[1])
words[#words] = firstToUpper(words[#words])
-- Concat elements in list via space character
local result = ""
for index, word in pairs(words) do
result = result .. word
if(index ~= #words) then
result = result .. ' '
end
end
return result
end
print(titlecase("the world"))
print(titlecase("I walked my dog this morning ..."))
print(titlecase("The art of Lua"))
--- Output:
----------------------
--- The World
--- I Walked My Dog This Morning ...
--- The Art of Lua