Password generator function in R - string

I am looking for a smart way to code a password generator function in R:
generate.password (length, capitals, numbers)
length: the length of the password
capitals: a vector of defining where capitals shall occur, vector reflects the corresponsing password string position, default should be no capitals
numbers: a vector defining where capitals shall occur, vector reflects the corresponsing password string position, default should be no numbers
Examples:
generate.password(8)
[1] "hqbfpozr"
generate.password(length=8, capitals=c(2,4))
[1] "hYbFpozr"
generate.password(length=8, capitals=c(2,4), numbers=c(7:8))
[1] "hYbFpo49"

There is function which generates random strings in the stringi (version >= 0.2-3) package:
require(stringi)
stri_rand_strings(n=2, length=8, pattern="[A-Za-z0-9]")
## [1] "90i6RdzU" "UAkSVCEa"
So using different patterns you can generate parts for your desired password and then paste it like this:
x <- stri_rand_strings(n=4, length=c(2,1,2,3), pattern=c("[a-z]","[A-Z]","[0-9]","[a-z]"))
x
## [1] "ex" "N" "81" "tsy"
stri_flatten(x)
## [1] "exN81tsy"

Here's one approach
generate.password <- function(length,
capitals = integer(0),
numbers = integer(0)) {
stopifnot(is.numeric(length), length > 0L,
is.numeric(capitals), capitals > 0L, capitals <= length,
is.numeric(numbers), numbers > 0L, numbers <= length,
length(intersect(capitals, numbers)) == 0L)
lc <- sample(letters, length, replace = TRUE)
uc <- sample(LETTERS, length(capitals), replace = TRUE)
num <- sample(0:9, length(numbers), replace = TRUE)
pass <- lc
pass[capitals] <- uc
pass[numbers] <- num
paste0(pass, collapse = "")
}
## Examples
set.seed(1)
generate.password(8)
# [1] "gjoxfxyr"
set.seed(1)
generate.password(length=8, capitals=c(2,4))
# [1] "gQoBfxyr"
set.seed(1)
generate.password(length=8, capitals=c(2,4), numbers=c(7:8))
# [1] "gQoBfx21"
You can also add other special characters in the same fashion. If you want repeated values for letters and numbers, then add replace =TRUE in sample function.

I liked the solution given by #Hadd E. Nuff... and What I did, is the inclusion of digits between 0 and 9, at random... here is the modified solution...
generate.password <- function(LENGTH){
punct <- c("!", "#", "$", "%", "&", "(", ")", "*", "+", "-", "/", ":",
";", "<", "=", ">", "?", "#", "[", "^", "_", "{", "|", "}", "~")
nums <- c(0:9)
chars <- c(letters, LETTERS, punct, nums)
p <- c(rep(0.0105, 52), rep(0.0102, 25), rep(0.02, 10))
pword <- paste0(sample(chars, LENGTH, TRUE, prob = p), collapse = "")
return(pword)
}
generate.password(8)
This will generate very strong passwords like:
"C2~mD20U" # 8 alpha-numeric-specialchar
"+J5Gi3" # 6 alpha-numeric-specialchar
"77{h6RsGQJ66if5" # 15 alpha-numeric-specialchar

Related

Generating substrings and random strings in R

Please bear with me, I come from a Python background and I am still learning string manipulation in R.
Ok, so lets say I have a string of length 100 with random A, B, C, or D letters:
> df<-c("ABCBDBDBCBABABDBCBCBDBDBCBDBACDBCCADCDBCDACDDCDACBCDACABACDACABBBCCCBDBDDCACDDACADDDDACCADACBCBDCACD")
> df
[1]"ABCBDBDBCBABABDBCBCBDBDBCBDBACDBCCADCDBCDACDDCDACBCDACABACDACABBBCCCBDBDDCACDDACADDDDACCADACBCBDCACD"
I would like to do the following two things:
1) Generate a '.txt' file that is comprised of 20-length subsections of the above string, each starting one letter after the previous with their own unique name on the line above it, like this:
NAME1
ABCBDBDBCBABABDBCBCB
NAME2
BCBDBDBCBABABDBCBCBD
NAME3
CBDBDBCBABABDBCBCBDB
NAME4
BDBDBCBABABDBCBCBDBD
... and so forth
2) Take that generated list and from it comprise another list that has the same exact substrings with the only difference being a change of one or two of the A, B, C, or Ds to another A, B, C, or D (any of those four letters only).
So, this:
NAME1
ABCBDBDBCBABABDBCBCB
Would become this:
NAME1.1
ABBBDBDBCBDBABDBCBCB
As you can see, the "C" in the third position became a "B" and the "A" in position 11 became a "D", with no implied relationship between those changed letters. Purely random.
I know this is a convoluted question, but like I said, I am still learning basic text and string manipulation in R.
Thanks in advance.
Create a text file of substrings
n <- 20 # length of substrings
starts <- seq(nchar(df) - 20 + 1)
v1 <- mapply(substr, starts, starts + n - 1, MoreArgs = list(x = df))
names(v1) <- paste0("NAME", seq_along(v1), "\n")
write.table(v1, file = "filename.txt", quote = FALSE, sep = "",
col.names = FALSE)
Randomly replace one or two letters (A-D):
myfun <- function() {
idx <- sample(seq(n), sample(1:2, 1))
rep <- sample(LETTERS[1:4], length(idx), replace = TRUE)
return(list(idx = idx, rep = rep))
}
new <- replicate(length(v1), myfun(), simplify = FALSE)
v2 <- mapply(function(x, y, z) paste(replace(x, y, z), collapse = ""),
strsplit(v1, ""),
lapply(new, "[[", "idx"),
lapply(new, "[[", "rep"))
names(v2) <- paste0(names(v2), ".1")
write.table(v2, file = "filename2.txt", quote = FALSE, sep = "\n",
col.names = FALSE)
I tried breaking this down into multiple simple steps, hopefully you can get learn a few tricks from this:
# Random data
df<-c("ABCBDBDBCBABABDBCBCBDBDBCBDBACDBCCADCDBCDACDDCDACBCDACABACDACABBBCCCBDBDDCACDDACADDDDACCADACBCBDCACD")
n<-10 # Number of cuts
set.seed(1)
# Pick n random numbers between 1 and the length of string-20
nums<-sample(1:(nchar(df)-20),n,replace=TRUE)
# Make your cuts
cuts<-sapply(nums,function(x) substring(df,x,x+20-1))
# Generate some names
nams<-paste0('NAME',1:n)
# Make it into a matrix, transpose, and then recast into a vector to get alternating names and cuts.
names.and.cuts<-c(t(matrix(c(nams,cuts),ncol=2)))
# Drop a file.
write.table(names.and.cuts,'file.txt',quote=FALSE,row.names=FALSE,col.names = FALSE)
# Pick how many changes are going to be made to each cut.
changes<-sample(1:2,n,replace=2)
# Pick that number of positions to change
pos.changes<-lapply(changes,function(x) sample(1:20,x))
# Find the letter at each position.
letter.at.change.pos<-lapply(pos.changes,function(x) substring(df,x,x))
# Make a function that takes any letter, and outputs any other letter from c(A-D)
letter.map<-function(x){
# Make a list of alternate letters.
alternates<-lapply(x,setdiff,x=c('A','B','C','D'))
# Pick one of each
sapply(alternates,sample,size=1)
}
# Find another letter for each
letter.changes<-lapply(letter.at.change.pos,letter.map)
# Make a function to replace character by position
# Inefficient, but who cares.
rep.by.char<-function(str,pos,chars){
for (i in 1:length(pos)) substr(str,pos[i],pos[i])<-chars[i]
str
}
# Change every letter at pos.changes to letter.changes
mod.cuts<-mapply(rep.by.char,cuts,pos.changes,letter.changes,USE.NAMES=FALSE)
# Generate names
nams<-paste0(nams,'.1')
# Use the matrix trick to alternate names.Drop a file.
names.and.mod.cuts<-c(t(matrix(c(nams,mod.cuts),ncol=2)))
write.table(names.and.mod.cuts,'file2.txt',quote=FALSE,row.names=FALSE,col.names = FALSE)
Also, instead of the rep.by.char function, you could just use strsplit and replace like this:
mod.cuts<-mapply(function(x,y,z) paste(replace(x,y,z),collapse=''),
strsplit(cuts,''),pos.changes,letter.changes,USE.NAMES=FALSE)
One way, albeit slowish:
Rgames> foo<-paste(sample(c('a','b','c','d'),20,rep=T),sep='',collapse='')
Rgames> bar<-matrix(unlist(strsplit(foo,'')),ncol=5)
Rgames> bar
[,1] [,2] [,3] [,4] [,5]
[1,] "c" "c" "a" "c" "a"
[2,] "c" "c" "b" "a" "b"
[3,] "b" "b" "a" "c" "d"
[4,] "c" "b" "a" "c" "c"
Now you can select random indices and replace the selected locations with sample(c('a','b','c','d'),1) . For "true" randomness, I wouldn't even force a change - if your newly drawn letter is the same as the original, so be it.
Like this:
ibar<-sample(1:5,4,rep=T) # one random column number for each row
for ( j in 1: 4) bar[j,ibar[j]]<-sample(c('a','b','c','d'),1)
Then, if necessary, recombine each row using paste
For the first part of your question:
df <- c("ABCBDBDBCBABABDBCBCBDBDBCBDBACDBCCADCDBCDACDDCDACBCDACABACDACABBBCCCBDBDDCACDDACADDDDACCADACBCBDCACD")
nstrchars <- 20
count<- nchar(df)-nstrchars
length20substrings <- data.frame(length20substrings=sapply(1:count,function(x)substr(df,x,x+20)))
# to save to a text file. I chose not to include row names or a column name in the .txt file file
write.table(length20substrings,"length20substrings.txt",row.names=F,col.names=F)
For the second part:
# create a function that will randomly pick one or two spots in a string and replace
# those spots with one of the other characters present in the string:
changefxn<- function(x){
x<-as.character(x)
nc<-nchar(as.character(x))
id<-seq(1,nc)
numchanges<-sample(1:2,1)
ids<-sample(id,numchanges)
chars2repl<-strsplit(x,"")[[1]][ids]
charspresent<-unique(unlist(strsplit(x,"")))
splitstr<-unlist(strsplit(x,""))
if (numchanges>1) {
splitstr[id[1]] <- sample(setdiff(charspresent,chars2repl[1]),1)
splitstr[id[2]] <- sample(setdiff(charspresent,chars2repl[2]),1)
}
else {splitstr[id[1]] <- sample(setdiff(charspresent,chars2repl[1]),1)
}
newstr<-paste(splitstr,collapse="")
return(newstr)
}
# try it out
changefxn("asbbad")
changefxn("12lkjaf38gs")
# apply changefxn to all the substrings from part 1
length20substrings<-length20substrings[seq_along(length20substrings[,1]),]
newstrings <- lapply(length20substrings, function(ii)changefxn(ii))

How to paste two vectors together and pad at the end?

I would like to paste two character strings together and pad at the end with another character to make the combination a certain length. I was wondering if there was an option to paste that one can pass or another trick that I am missing? I can do this in multiple lines by figuring out the length of each and then calling paste with rep(my_pad_character,N) but I would like to do this in one line.
Ex: pad together "hi", and "hello" and pad with an "a" to make the sequence length 10. the result would be "hihelloaaa"
Here is one option:
s1 <- "hi"
s2 <- "hello"
f <- function(x, y, pad = "a", length = 10) {
out <- paste0(x, y)
nc <- nchar(out)
paste0(out, paste(rep(pad, length - nc), collapse = ""))
}
> f(s1, s2)
[1] "hihelloaaa"
You can use the stringr function str_pad
library(stringr)
str_pad(paste0('hi','hello'), side = 'right', width = 10 , pad = 'a')

Getting and removing the first character of a string

I would like to do some 2-dimensional walks using strings of characters by assigning different values to each character. I was planning to 'pop' the first character of a string, use it, and repeat for the rest of the string.
How can I achieve something like this?
x <- 'hello stackoverflow'
I'd like to be able to do something like this:
a <- x.pop[1]
print(a)
'h'
print(x)
'ello stackoverflow'
See ?substring.
x <- 'hello stackoverflow'
substring(x, 1, 1)
## [1] "h"
substring(x, 2)
## [1] "ello stackoverflow"
The idea of having a pop method that both returns a value and has a side effect of updating the data stored in x is very much a concept from object-oriented programming. So rather than defining a pop function to operate on character vectors, we can make a reference class with a pop method.
PopStringFactory <- setRefClass(
"PopString",
fields = list(
x = "character"
),
methods = list(
initialize = function(x)
{
x <<- x
},
pop = function(n = 1)
{
if(nchar(x) == 0)
{
warning("Nothing to pop.")
return("")
}
first <- substring(x, 1, n)
x <<- substring(x, n + 1)
first
}
)
)
x <- PopStringFactory$new("hello stackoverflow")
x
## Reference class object of class "PopString"
## Field "x":
## [1] "hello stackoverflow"
replicate(nchar(x$x), x$pop())
## [1] "h" "e" "l" "l" "o" " " "s" "t" "a" "c" "k" "o" "v" "e" "r" "f" "l" "o" "w"
There is also str_sub from the stringr package
x <- 'hello stackoverflow'
str_sub(x, 2) # or
str_sub(x, 2, str_length(x))
[1] "ello stackoverflow"
Use this function from stringi package
> x <- 'hello stackoverflow'
> stri_sub(x,2)
[1] "ello stackoverflow"
substring is definitely best, but here's one strsplit alternative, since I haven't seen one yet.
> x <- 'hello stackoverflow'
> strsplit(x, '')[[1]][1]
## [1] "h"
or equivalently
> unlist(strsplit(x, ''))[1]
## [1] "h"
And you can paste the rest of the string back together.
> paste0(strsplit(x, '')[[1]][-1], collapse = '')
## [1] "ello stackoverflow"
removing first characters:
x <- 'hello stackoverflow'
substring(x, 2, nchar(x))
Idea is select all characters starting from 2 to number of characters in x. This is important when you have unequal number of characters in word or phrase.
Selecting the first letter is trivial as previous answers:
substring(x,1,1)
Another alternative is to use capturing sub-expressions with the regular expression functions regmatches and regexec.
# the original example
x <- 'hello stackoverflow'
# grab the substrings
myStrings <- regmatches(x, regexec('(^.)(.*)', x))
This returns the entire string, the first character, and the "popped" result in a list of length 1.
myStrings
[[1]]
[1] "hello stackoverflow" "h" "ello stackoverflow"
which is equivalent to list(c(x, substr(x, 1, 1), substr(x, 2, nchar(x)))). That is, it contains the super set of the desired elements as well as the full string.
Adding sapply will allow this method to work for a character vector of length > 1.
# a slightly more interesting example
xx <- c('hello stackoverflow', 'right back', 'at yah')
# grab the substrings
myStrings <- regmatches(x, regexec('(^.)(.*)', xx))
This returns a list with the matched full string as the first element and the matching subexpressions captured by () as the following elements. So in the regular expression '(^.)(.*)', (^.) matches the first character and (.*) matches the remaining characters.
myStrings
[[1]]
[1] "hello stackoverflow" "h" "ello stackoverflow"
[[2]]
[1] "right back" "r" "ight back"
[[3]]
[1] "at yah" "a" "t yah"
Now, we can use the trusty sapply + [ method to pull out the desired substrings.
myFirstStrings <- sapply(myStrings, "[", 2)
myFirstStrings
[1] "h" "r" "a"
mySecondStrings <- sapply(myStrings, "[", 3)
mySecondStrings
[1] "ello stackoverflow" "ight back" "t yah"
Another way using the sub function.
sub('(^.).*', '\\1', 'hello stackoverflow')
[1] "h"
sub('(^.)(.*)', '\\2', 'hello stackoverflow')
[1] "ello stackoverflow"

R: How can I replace let's say the 5th element within a string?

I would like to convert the a string like be33szfuhm100060 into BESZFUHM0060.
In order to replace the small letters with capital letters I've so far used the gsub function.
test1=gsub("be","BE",test)
Is there a way to tell this function to replace the 3rd and 4th string element? If not, I would really appreciate if you could tell me another way to solve this problem. Maybe there is also a more general solution to change a string element at a certain position into a capital letter whatever the element is?
A couple of observations:
Cnverting a string to uppercase can be done with toupper, e.g.:
> toupper('be33szfuhm100060')
> [1] "BE33SZFUHM100060"
You could use substr to extract a substring by character positions and paste to concatenate strings:
> x <- 'be33szfuhm100060'
> paste(substr(x, 1, 2), substr(x, 5, nchar(x)), sep='')
[1] "beszfuhm100060"
As an alternative, if you are going to be doing this alot:
String <- function(x="") {
x <- as.character(paste(x, collapse=""))
class(x) <- c("String","character")
return(x)
}
"[.String" <- function(x,i,j,...,drop=TRUE) {
unlist(strsplit(x,""))[i]
}
"[<-.String" <- function(x,i,j,...,value) {
tmp <- x[]
tmp[i] <- String(value)
x <- String(tmp)
x
}
print.String <- function(x, ...) cat(x, "\n")
## try it out
> x <- String("be33szfuhm100060")
> x[3:4] <- character(0)
> x
beszfuhm100060
You can use substring to remove the third and fourth elements.
x <- "be33szfuhm100060"
paste(substring(x, 1, 2), substring(x, 5), sep = "")
If you know what portions of the string you want based on their position(s), use substr or substring. As I mentioned in my comment, you can use toupper to coerce characters to uppercase.
paste( toupper(substr(test,1, 2)),
toupper(substr(test,5,10)),
substr(test,12,nchar(test)),sep="")
# [1] "BESZFUHM00060"

Implement ROT-13 in R

I'd like a function, that when passed a string containing only letters, rotates each letter in the string through the alphabet by X characters, where X is a parameter of the function. The famous instance of this is when X=13, which is called ROT-13
function <- ROTx(str,x) {
??
}
It's the kind of thing that I'd expect an R wizard could do in just a few lines, whereas I'd end up with 10 or more.
See ?chartr (Examples section):
rot <- function(ch, k = 13) {
p0 <- function(...) paste(c(...), collapse="")
A <- c(letters, LETTERS, " '")
I <- seq_len(k)
chartr(p0(A), p0(c(A[-I], A[I])), ch)
}
or here http://rosettacode.org/wiki/Rot-13#R:
rot13 <- function(x)
{
old <- paste(letters, LETTERS, collapse="", sep="")
new <- paste(substr(old, 27, 52), substr(old, 1, 26), sep="")
chartr(old, new, x)
}
rotX <- function(ch,x)
{ #rotate each letter of a string ch by x letters thru the alphabet, as long as x<=13
old <- paste(letters, LETTERS, collapse="", sep="")
new <- paste(substr(old, 2*x+1, 26*2), substr(old, 1, 26), sep="")
chartr(old, new, ch)
}
This fixes both of the problems I noted in my comment.

Resources