Reputation: 1025
Im having 2 problems when trying to generate a random string in Linux with Swift 3.
arc4random_uniform is not available in Linux only on BSD. SO i was able to get away with using random() function. And this worked when i was generating random numbers of a variable size (See code below)
func generateRandomNumber() -> Int
{
var place = 1
var finalNumber = 0;
#if os(Linux)
for _ in 0..<5
{
place *= 10
let randomNumber = Int(random() % 10) + 1
finalNumber += randomNumber * place
}
#else
for _ in 0..<5
{
place *= 10
let randomNumber = Int(arc4random_uniform(10))
finalNumber += randomNumber * place
}
#endif
return finalNumber
}
And that WORKS. Edit: it works but it gives me the same number every time :(
original pre Linux block of code:
func randomString(_ length: Int) -> String
{
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.length)
var randomString = ""
for _ in 0 ..< length {
let rand = arc4random_uniform(len)
var nextChar = letters.character(at: Int(rand))
randomString += NSString(characters: &nextChar, length: 1) as String
}
return randomString
}
And the actual error I get when using above code
error: cannot convert value of type 'NSString' to type 'String' in coercion
randomString += NSString(characters: &nextChar, length: 1) as String
modified for linux block of code.
func randomString(_ length: Int) -> String
{
let letters : String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = letters.characters.count
var randomString = ""
#if os(Linux)
for _ in 0..<length
{
let randomValue = (random() % len) + 1
randomString += "\(letters[letters.index(letters.startIndex, offsetBy: Int(randomValue))])"
}
#else
for _ in 0 ..< length
{
let rand = arc4random_uniform(UInt32(len))
randomString += "\(letters[letters.index(letters.startIndex, offsetBy: Int(rand))])"
}
#endif
return randomString
}
but this time the error is weird it only says Illegal instruction with no extra information. I ran the docker container in interactive mode and i saw my server running and printing out when calling other functions etc.
but the thing is the function actually WORKS when i ran it in IBMs swift sandbox
and I'm assuming its using linux also. Im very stuck and confused any help would be greatly appreciated.
(UPDATE): I ran the same function in just a linux env with a single swift file and not the Vapor swift web framework. and it works. As mentioned in my edit above it gives me the same random string everytime. I will still have to test the entire project once my build finishes. But besides that i need to know if the random() function will actually give me something new each time instead of the same crap.
Upvotes: 3
Views: 928
Reputation: 2348
Swift 4.2, Ubuntu 16.04
let letters : String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = letters.count
var randomString:String = ""
for _ in 0 ..< length {
let rand = Int.random(in: 0..<len)
randomString += letters.map { String($0) }[rand]
}
Upvotes: 0
Reputation: 1367
I copied and pasted your code exactly, and it doesn't compile.
fatal error: Can't form a Character from an empty String
// Keep at top of your code (outside of functions)
#if os(Linux)
srandom(UInt32(time(nil)))
#endif
func getRandomNumber(_ min: Int, _ max: Int) -> Int {
#if os(Linux)
return Int(random() % max) + min
#else
return Int(arc4random_uniform(UInt32(max)) + UInt32(min))
#endif
}
func getRandomString(_ chars: String, _ length: Int) -> String {
var str = ""
for _ in 1...length {
str.append(chars.itemOnStartIndex(advancedBy: getRandomNumber(0, chars.count - 1)))
}
return str
}
// Best practice to define this outside of the function itself
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
print(getRandomString(chars, 10))
This works for me on Ubuntu.
Upvotes: 0
Reputation: 19602
1) Always the same number
You have to set a seed once to get "random" numbers from random()
:
randomSeed(Int(Date().timeIntervalSince1970)
If no seed value is provided, the random() function is automatically seeded with a value of 1.
As the seed is always the same (1), you always get the same sequence of "random" numbers.
2) Alphanumeric string
To create your string without using NSString
:
func randomString(length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.characters.count)
var randomString = ""
for _ in 0 ..< length {
let rand = myCustomRandom(len)
let randIndex = letters.index(letters.startIndex, offsetBy: Int(rand))
let nextChar = letters[randIndex]
randomString += String(nextChar)
}
return randomString
}
Upvotes: 3
Reputation: 1025
Figured it out.
So the answer to the repeating random number/string was to just add this line before i called the random() function
srand(UInt32(time(nil)))
and I'm assuming thats what fixed the illegal instruction also. Because i don't recall changing anything else.
Needless to say here is my final result
func generateRandomNumber() -> Int
{
var place = 1
var finalNumber = 0;
#if os(Linux)
srand(UInt32(time(nil)))
for _ in 0..<5
{
place *= 10
let randomNumber = Int(random() % 10) + 1
finalNumber += randomNumber * place
}
#else
for _ in 0..<5
{
place *= 10
let randomNumber = Int(arc4random_uniform(10))
finalNumber += randomNumber * place
}
#endif
return finalNumber
}
func randomString(_ length: Int) -> String
{
let letters : String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = letters.characters.count
var randomString = ""
#if os(Linux)
srand(UInt32(time(nil)))
for _ in 0..<length
{
let randomValue = (random() % len) + 1
randomString += "\(letters[letters.index(letters.startIndex, offsetBy: Int(randomValue))])"
}
#else
for _ in 0 ..< length
{
let rand = arc4random_uniform(UInt32(len))
randomString += "\(letters[letters.index(letters.startIndex, offsetBy: Int(rand))])"
}
#endif
return randomString
}
Upvotes: 3