How can I generate an n-character pseudo random string containing only A-Z, 0-9 like SecureRandom.base64 without "+", "/", and "="? For example:
(0..n).map {(('1'..'9').to_a + ('A'..'Z').to_a)[rand(36)]}.join
Array.new(n){[*"A".."Z", *"0".."9"].sample}.join
An elegant way to do it in Rails 5 (I don't test it in another Rails versions):
SecureRandom.urlsafe_base64(n)
where n is the number of digits that you want.
ps: SecureRandom uses a array to mount your alphanumeric string, so keep in mind that n should be the amount of digits that you want + 1.
ex: if you want a 8 digit alphanumeric:
SecureRandom.urlsafe_base64(9)
Even brute force is pretty easy:
n = 20
c = [*?A..?Z + *?0..?9]
size = c.size
n.times.map { c[rand(size)] }.join
#=> "IE210UOTDSJDKM67XCG1"
or, without replacement:
c.sample(n).join
#=> "GN5ZC0HFDCO2G5M47VYW"
should that be desired. (I originally had c = [*(?A..?Z)] + [*(?0..?9)], but saw from #sawa's answer that that could be simplified quite a bit.)
To generate a random string from 10 to 20 characters including just from A to Z and numbers, both always:
require 'string_pattern'
puts "10-20:/XN/".gen
You can do simply like below:
[*'A'..'Z', *0..9].sample(10).join
Change the number 10 to any number to change the length of string
Related
I have strings that consist of two lots of numbers. I am trying to split them into two substrings so that there are two decimal places after each decimal.
I have this code:
if homeodds.length == 10 then
homeoddsp = homeodds[0,5].to_f
bookieh = homeodds[5,5].to_f
else
homeoddsp = homeodds[0,4].to_f
bookieh = homeodds[4,4].to_f
end
It handles an eight figure string fine:
"1.211.90" translates to "1.21" and "1.90".
and a ten figure string also works:
"12.2113.00" translates to "12.21" and "13.00".
When I have a nine figure string like "9.1110.00" or "10.119.55", I need to find out where the first decimal is, and take two digits after that so that the remainder becomes the second substring. These cases, I can't consistently do.
You could use String#scan for this particular problem
def decimal_splits(string)
string.scan(/\d+\.\d{2}/)
end
To break down the regular expression:
\d+ 1 or more number of digits
\. the decimal point
\d{2} two digits
The result will be an array of matches for the expression.
decimal_splits("9.1110.00")
#=> ["9.11", "10.00"]
decimal_splits("12.2113.00")
#=> ["12.21", "13.00"]
decimal_splits("1.211.90")
#=> ["1.21", "1.90"]
decimal_splits("10.119.55")
#=> ["10.11", "9.55"]
I have a long list of small numbers, all of them < 16 but there can be more than 10000 of them in a unique list.
I get the values as a comma separated list, like:
6,12,10,2,2,2,6,12,8,2,2,6,10,2,4,12,14,10,2, .... lots and lots of numbers
And finally I need to store the values in a database in the most efficient way in order to be read back and processed again ... as a string, comma separated values.
I was thinking of sort of storing them in a big TEXT field ... however I find that adding all the commas in there would be a waste of space.
I am wondering if there is any best practice for this scenario.
For more technical details:
for Database I have to use Postgres (and I am sort of beginner in this field), and the programming language is Ruby (also beginner :) )
For a fast and reasonably space efficient solution, you could simply write a hexadecimal string :
string = '6,12,10,2,2,2,6,12,8,2,2,6,10,2,4,12,14,10,2'
p string.split(',').map { |v| v.to_i.to_s(16) }.join
# "6ca2226c8226a24cea2"
p '6ca2226c8226a24cea2'.each_char.map { |c| c.to_i(16) }.join(',')
# "6,12,10,2,2,2,6,12,8,2,2,6,10,2,4,12,14,10,2"
It brings the advantage of being easily readable by any DB and any program.
Also, it works even if there are leading 0s in the string : "0,0,6".
If you have an even number of elements, you could pack 2 hexa characters into one byte, to divide the string length by 2.
numbers = "6,12,10,2,2,2,6,12,8,2,2,6,10,2,4,12,14,10,2"
numbers.split(',')
.map { |n| n.to_i.to_s(2).rjust(4, '0') }
.join
.to_i(2)
.to_s(36)
#⇒ "57ymwcgbl1umt2a"
"57ymwcgbl1umt2a".to_i(36)
.to_s(2)
.tap { |e| e.prepend('0') until (e.length % 4).zero? }
.scan(/.{4}/)
.map { |e| e.to_i(2).to_s }
.join(',')
#⇒ "6,12,10,2,2,2,6,12,8,2,2,6,10,2,4,12,14,10,2"
How do I easily convert a number, e.g. 0x616263, equivalently 6382179 in base 10, into a string by dividing the number up into sequential bytes? So the example above should convert into 'abc'.
I've experimented with Array.pack but cant figure out how to get it to convert more than one byte in the number, e.g. [0x616263].pack("C*") returns 'c'.
I've also tried 0x616263.to_s(256), but that throws an ArgumentError: invalid radix. I guess it needs some sort of encoding information?
(Note: Other datatypes in pack like N work with the example I've given above, but only because it fits within 4 bytes, so e.g. [0x616263646566].pack("N") gives cdef, not abcdef)
This question is vaguely similar to this one, but not really. Also, I sort of figured out how to get the hex representation string from a character string using "abcde".unpack("c*").map{|c| c.to_s(16)}.join(""), which gives '6162636465'. I basically want to go backwards.
I don't think this is an X-Y problem, but in case it is - I'm trying to convert a number I've decoded with RSA into a character string.
Thanks for any help. I'm not too experienced with Ruby. I'd also be interested in a Python solution (for fun), but I don't know if its right to add tags for two separate programming languages to this question.
To convert a single number 0x00616263 into 3 characters, what you really need to do first is separate them into three numbers: 0x00000061, 0x00000062, and 0x00000063.
For the last number, the hex digits you want are already in the correct place. But for the other two, you have to do a bitshift using >> 16 and >> 8 respectively.
Afterwards, use a bitwise and to get rid of the other digits:
num1 = (0x616263 >> 16) & 0xFF
num2 = (0x616263 >> 8) & 0xFF
num3 = 0x616263 & 0xFF
For the characters, you could then do:
char1 = ((0x616263 >> 16) & 0xFF).chr
char2 = ((0x616263 >> 8) & 0xFF).chr
char3 = (0x616263 & 0xFF).chr
Of course, bitwise operations aren't very Ruby-esque. There are probably more Ruby-like answers that someone else might provide.
64 bit integers
If your number is smaller than 2**64 (8 bytes), you can :
convert the "big-endian unsigned long long" to 8 bytes
remove the leading zero bytes
Ruby
[0x616263].pack('Q>').sub(/\x00+/,'')
# "abc"
[0x616263646566].pack('Q>').sub(/\x00+/,'')
# "abcdef"
Python 2 & 3
In Python, pack returns bytes, not a string. You can use decode() to convert bytes to a String :
import struct
import re
print(re.sub('\x00', '', struct.pack(">Q", 0x616263646566).decode()))
# abcdef
print(re.sub('\x00', '', struct.pack(">Q", 0x616263).decode()))
# abc
Large numbers
With gsub
If your number doesn't fit in 8 bytes, you could use a modified version of your code. This is shorter and outputs the string correctly if the first byte is smaller than 10 (e.g. for "\t") :
def decode(int)
if int < 2**64
[int].pack('Q>').sub(/\x00+/, '')
else
nhex = int.to_s(16)
nhex = '0' + nhex if nhex.size.odd?
nhex.gsub(/../) { |hh| hh.to_i(16).chr }
end
end
puts decode(0x616263) == 'abc'
# true
puts decode(0x616263646566) == 'abcdef'
# true
puts decode(0x0961) == "\ta"
# true
puts decode(0x546869732073656e74656e63652069732077617920746f6f206c6f6e6720666f7220616e20496e743634)
# This sentence is way too long for an Int64
By the way, here's the reverse method :
def encode(str)
str.reverse.each_byte.with_index.map { |b, i| b * 256**i }.inject(:+)
end
You should still check if your RSA code really outputs arbitrary large numbers or just an array of integers.
With shifts
Here's another way to get the result. It's similar to #Nathan's answer, but it works for any integer size :
def decode(int)
a = []
while int>0
a << (int & 0xFF)
int >>= 8
end
a.reverse.pack('C*')
end
According to fruity, it's twice as fast as the gsub solution.
I'm currently rolling with this:
n = 0x616263
nhex = n.to_s(16)
nhexarr = nhex.scan(/.{1,2}/)
nhexarr = nhexarr.map {|e| e.to_i(16)}
out = nhexarr.pack("C*")
But was hoping for a concise/built-in way to do this, so I'll leave this answer unaccepted for now.
Right now I am working on a project that issues IDs consisting of both letters and numbers, for example 345A22. I need this program to be able to tell that for example, 345B22 is greater than 345A22. I can't assume that the letters will be in the same position all the time (ie we do have some id's with 22335Q) but when I compare two numbers the letters will be in the same position.
How do I accomplish this in Ruby?
You can use the String#<=> method to compare strings. See documentation here.
>> "345B22" <=> "345A22"
=> 1
Where the 1 return value means that 345B22 is greater.
If a simple string comparison won't do the trick (e.g. different lengths, etc.), try converting the IDs (assuming they all match ^[0-9A-Z]*$) into integers by treating them as base36-encoded data.
In Ruby strings have the same comparison methods as numbers have.
2 > 1 #=> true
"2" > "1" #=> true
"B" > "A" #=> true
Not sure I understand your question, but I'm guessing that you mentally parse the ids into components (so 345B22 is 345, B, 22) and then are wishing for a numeric sort for things that are numbers (i.e., 12 > 2) and a string sort for things that are strings (AB < B).
If this is what you intend, something like the following would do the trick:
ids.sort_by do |id|
id.scan(/\d+|[a-zA-Z]+/).map {|c| c =~ /\d/ ? c.rjust(20) : c.ljust(20) }.join
end
What this does is extract out all consecutive numbers or letters and then justify them right or left based on their type, concatenates the result and then sorts based on this (expanded and canonicalized) id.
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.