Swift adding multiple stringByReplacingOccurrencesOfString on one String? - xcode

Hello i would like to create a app that changes characters into binary code and i was wondering if there is a way to add multiple stringByReplacingOccurrencesOfString on one String or if i should take another approach to this "Problem".
Here is what i have so far
func textToBinary(theString: String) -> String {
return theString.stringByReplacingOccurrencesOfString("a",
withString: "01100001")
}
textArea.text = textToBinary(lettersCombined)
// lettersCombined is the string that i want to turn into BinaryCode.

Try this:
func textToBinary(theString : String, radix : Int = 2) -> String {
var result = ""
for c in theString.unicodeScalars {
result += String(c.value, radix: radix) + " "
}
return result
}
println(textToBinary("a"))
println(textToBinary("abc", radix: 10))
println(textToBinary("€20", radix: 16))
println(textToBinary("😄"))
(The last one is a smiley face but somehow my browser can't display it).
Edit: if you want to pad your strings to 8-character long, try this:
let str = "00000000" + String(c.value, radix: radix)
result += str.substringFromIndex(advance(str.startIndex, str.characters.count - 8)) + " "
The first line adds eight 0 the left of your string. The second line takes the last 8 characters from the padded string.

Related

Parse log entries with least possible overhead

I have some log format with entries like this:
log_entry_no1 := "2021-11-03 7:7:51 hal9000 evil_app heartbeat C99 I am sorry Dave"
Those "fields" are separated by space except the last one called message which is just log data and can contain spaces.
My question is. Is there better way to process those entries without first splitting whole sentence and then join'ing that last part (message) with less overhead using go?
type LogData struct {
d Date // yyyy-mm-dd Mandatory
t Time // hh:mm:ss Mandatory
hostname string // Mandatory
app_id string // Mandatory
etype string // enum based string Mandatory
level string // Optional base on etype
message string // Mandatory
}
log_fields := strings.Split(log_entry_no1, " ")
var log_data = LogData{}
log_data.d = parseTime(log_entry_no1[0])
log_data.t = parseTime(log_entry_no1[1])
//...
if log_fields[4] == "heartbeat" {
log_data.level = log_fields[5]
log_data.message = strings.Join(log_fields[6:], " ")
} else {
log_data.message = strings.Join(log_fields[5:], " ")
}
Use strings.SplitN.
func SplitN(s, sep string, n int) []string
The docs say if n is greater than zero: it returns at most n substrings; the last substring will be the unsplit remainder.

How to ensure minimum occurrences of RNG outcomes?

I'm working on a basic password generator for This Reddit Programming Exercise. I have very simple password generation working in Kotlin, but I'm struggling to come up with a good way to guarantee a specific minimum number of each character type (i.e. at least 2 each of Upper, Lower, Digit, and special character).
I could easily just do two of each to start the password, but I want them to be randomly distributed, not all front-loaded. I also thought of inserting the characters randomly into a character array, rather than appending to a string, but I was wondering if maybe there is a better way.
Here is what I have currently:
private fun generatePassword() {
var password = ""
val passwordLength = (8..25).random()
for (i in 0..passwordLength) {
val charType = (1..5).random()
when (charType) {
1 -> password += lettersLower[(0..lettersLower.size).random()]
2 -> password += lettersUpper[(0..lettersUpper.size).random()]
3 -> password += numbers[(0..numbers.size).random()]
4 -> password += symbols[(0..symbols.size).random()]
}
}
println(password)
}
This works and generates a random password of a random length of 8-24 characters, and contains a random combination of uppercase letters, lowercase letters, digits, and special characters, but it doesn't guarantee the presence of any specific number of each type, like most websites require nowadays.
I thought of doing something like this:
private fun generatePassword() {
var password: ArrayList<Char> = arrayListOf()
var newChar:Char
var numLow = 0
var numUpp = 0
var numDig = 0
var numSpe = 0
while (numLow < 2){
newChar = lettersLower[(0..lettersLower.size).random()]
password.add((0..password.size).random(), newChar)
numLow++
}
while (numUpp < 2){
newChar = lettersUpper[(0..lettersUpper.size).random()]
password.add((0..password.size).random(), newChar)
numUpp++
}
while (numDig < 2){
newChar = numbers[(0..numbers.size).random()]
password.add((0..password.size).random(), newChar)
numDig++
}
while (numSpe < 2){
newChar = symbols[(0..symbols.size).random()]
password.add((0..password.size).random(), newChar)
numSpe++
}
val passwordLength = (8..25).random() - password.size
for (i in 0..passwordLength) {
val charType = (1..5).random()
when (charType) {
1 -> {
newChar = lettersLower[(0..lettersLower.size).random()]
password.add(newChar)
}
2 -> {
newChar = lettersUpper[(0..lettersUpper.size).random()]
password.add(newChar)
}
3 -> {
newChar = numbers[(0..numbers.size).random()]
password.add(newChar)
}
4 -> {
newChar = symbols[(0..symbols.size).random()]
password.add(newChar)
}
}
}
for (i in password.indices){
print(password[i])
}
print("\n")
}
But that doesn't seem particularly concise. The other option I thought of is to check if the password contains two of each type of character and generate a new one if it doesn't, but that seems awfully inefficient. Is there a better way to ensure that if I run my RNG 8+ times it will return at least, but not exactly, two ones, two twos, two threes, and two fours?
Here's my random function, if that helps:
// Random function via SO user #s1m0nw1 (https://stackoverflow.com/a/49507413)
private fun ClosedRange<Int>.random() = (Math.random() * (endInclusive - start) + start).toInt()
I also found this SO post in Java, but it's also not very concise and I'm wondering if there is a more general purpose, more concise solution to ensure a minimum number of occurrences for each possible outcome of an RNG over a number of trials greater than the sum of those minimum occurrences.
Sorry for the rambling post. Thanks in advance for your insight.
I think your second version is on the right track. It can be simplified to:
import java.util.Random
fun generatePassword() : String {
// this defines the classes of characters
// and their corresponding minimum occurrences
val charClasses = arrayOf(
('a'..'z').asIterable().toList() to 2,
('A'..'Z').asIterable().toList() to 2,
('0'..'9').asIterable().toList() to 2,
"!##$%^&*()_+".asIterable().toList() to 2
)
val maxLen = 24;
val minLen = 8;
var password = StringBuilder()
val random = Random()
charClasses.forEach {(chars, minOccur) ->
(0..minOccur).forEach {
password.insert(
if (password.isEmpty()) 0 else random.nextInt(password.length),
chars[random.nextInt(chars.size)])
}
}
((password.length + 1)..(random.nextInt(maxLen - minLen + 1) + minLen)).forEach {
val selected = charClasses[random.nextInt(charClasses.size)]
password.append(selected.first[random.nextInt(selected.first.size)])
}
return password.toString()
}

Swift 1.2, capture character from a word

My problem is how to get character from a word
The result I needed is
DisplayChar("asd",1)
and it will display "a"
func DisplayChar(word : String, number : Int) -> String{
let i: Int = count(word)
var result = 0
result = i - (i - number)
var str = ""
var j = 0
for j = 0; j < result; j++ {
str = str + word[j]
}
return str
}
DisplayChar("xyz", 2)
This code should work
let sentence = "Hello world"
let characters = Array(sentence)
print(characters[0]) // "H"
There are a couple good solutions in this answer that may work, two good ones duplicated below.
Convert to Array
let word = "test"
var firstChar = Array(word)[0] // t
(Note: this assumes a UTF8 or ASCII encoded string, but that is likely fine for school.)
Create Your Own Extension
First an extension of String to handle subscripts:
extension String {
subscript (i: Int) -> Character {
return self[self.startIndex.advancedBy(i)]
}
subscript (i: Int) -> String {
return String(self[i] as Character)
}
subscript (r: Range<Int>) -> String {
let start = startIndex.advancedBy(r.startIndex)
let end = start.advancedBy(r.endIndex - r.startIndex)
return self[Range(start ..< end)]
}
}
Then you can just use:
let word = "test"
var firstChar = word[0] // t
Swift strings have a method called substringToIndex, "asd".substringToIndex(1) will return "a".
I'm not sure if it works on Swift 1.2, though.

How can I concatenate strings only if they have passed a logical statement in Swift?

My challenge is twofold:
To pick individual strings from an array of similar strings, but only if a boolean test has been passed first.
"Finally" I need to concatenate any/all of the strings generated into one complete text and the entire code must be in Swift.
Illustration: A back of the envelope code for illustration of logic:
generatedText.text =
case Int1 <= 50 && Int2 == 50
return generatedParagraph1 = pick one string at RANDOM from a an array1 of strings
case Int3 =< 100
return generatedParagraph2 = pick one string at RANDOM from a an array2 of strings
case Int4 == 100
return generatedParagraph3 = pick one string at RANDOM from a an array3 of strings
...etc
default
return "Nothing to report"
and concatenate the individual generatedParagraphs
Attempt: Code picks a random element within stringArray1, 2 and 3.
Example of what the code returns:
---> "Sentence1_c.Sentence2_a.Sentence3_b."
PROBLEM: I need the code to ONLY pick an element if it has first passed a boolean. It means that the final concatenated string (concastString) could be empty, just contain one element, or several depending on how many of the bools were True. Does anyone know how to do this?
import Foundation
var stringArray1 = ["","Sentence1_a.", "Sentence1_b.", "Sentence1_c."]
var stringArray2 = ["","Sentence2_a.", "Sentence2_b.", "Sentence2_c."]
var stringArray3 = ["","Sentence3_a.", "Sentence3_b.", "Sentence3_c."]
let count1 = UInt32(stringArray1.count)-1
let count2 = UInt32(stringArray2.count)-1
let count3 = UInt32(stringArray3.count)-1
var randomNumberOne = Int(arc4random_uniform(count1))+1
var randomNumberTwo = Int(arc4random_uniform(count2))+1
var randomNumberThree = Int(arc4random_uniform(count3))+1
let concatString = stringArray1[randomNumberOne] + stringArray2[randomNumberTwo] + stringArray3[randomNumberThree]
Okay, I didn't pass a Bool, but I show concatenating three random strings from a [String]. I ran this in a playground.
import Foundation
var stringArray = [String]()
for var i = 0; i < 100; i++ {
stringArray.append("text" + "\(i)")
}
func concat (array: [String]) -> String {
let count = UInt32(stringArray.count)
let randomNumberOne = Int(arc4random_uniform(count))
let randomNumberTwo = Int(arc4random_uniform(count))
let randomNumberThree = Int(arc4random_uniform(count))
let concatString = array[randomNumberOne] + array[randomNumberTwo] + array[randomNumberThree]
return concatString
}
let finalString = concat(stringArray)

Creating a unique filename from a list of alphanumeric strings

I apologize for creating a similar thread to many that are out there now, but I mainly wanted to also get some insight on some methods.
I have a list of Strings (could be just 1 or over a 1000)
Format = XXX-XXXXX-XX where each one is alphanumeric
I am trying to generate a unique string (currently 18 in length but probably could be longer ensuring not to maximize file length or path length) that I could reproduce if I have that same list. Order doesn't matter; although I may be interested if its easier to restrict the order as well.
My current Java code is follows (which failed today, hence why I am here):
public String createOutputFileName(ArrayList alInput, EnumFPFunction efpf, boolean pHeaders) {
/* create file name based on input list */
String sFileName = "";
long partNum = 0;
for (String sGPN : alInput) {
sGPN = sGPN.replaceAll("-", ""); //remove dashes
partNum += Long.parseLong(sGPN, 36); //(base 36)
}
sFileName = Long.toString(partNum);
if (sFileName.length() > 19) {
sFileName.substring(0, 18); //Max length of 19
}
return alInput;
}
So obviously just adding them did not work out so well I found out (also think I should take last 18 digits and not first 18)
Are there any good methods out there (possibly CRC related) that would work?
To assist with my key creation:
The first 3 characters are almost always numeric and would probably have many duplicate (out of 100, there may only be 10 different starting numbers)
These characters are not allowed - I,O
There will never be a character then a number in the last two alphachar subset.
I would use the system time. Here's how you might do it in Java:
public String createOutputFileName() {
long mills = System.currentTimeMillis();
long nanos = System.nanoTime();
return mills + " " + nanos;
}
If you want to add some information about the items and their part numbers, you can, of course!
======== EDIT: "What do I mean by batch object" =========
class Batch {
ArrayList<Item> itemsToProcess;
String inputFilename; // input to external process
boolean processingFinished;
public Batch(ArrayList<Item> itemsToProcess) {
this.itemsToProcess = itemsToProcess;
inputFilename = null;
processingFinished = false;
}
public void processWithExternal() {
if(inputFilename != null || processingFinished) {
throw new IllegalStateException("Cannot initiate process more than once!");
}
String base = System.currentTimeMillis() + " " + System.nanoTime();
this.inputFilename = base + "_input";
writeItemsToFile();
// however you build your process, do it here
Process p = new ProcessBuilder("myProcess","myargs", inputFilename);
p.start();
p.waitFor();
processingFinished = true;
}
private void writeItemsToFile() {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(inputFilename)));
int flushcount = 0;
for(Item item : itemsToProcess) {
String output = item.getFileRepresentation();
out.println(output);
if(++flushcount % 10 == 0) out.flush();
}
out.flush();
out.close();
}
}
In addition to GlowCoder's response, I have thought of another "decent one" that would work.
Instead of just adding the list in base 36, I would do two separate things to the same list.
In this case, since there is no way for negative or decimal numbers, adding every number and multiplying every number separately and concatenating these base36 number strings isn't a bad way either.
In my case, I would take the last nine digits of the added number and last nine of the multiplied number. This would eliminate my previous errors and make it quite robust. It obviously is still possible for errors once overflow starts occurring, but could also work in this case. Extending the allowable string length would make it more robust as well.
Sample code:
public String createOutputFileName(ArrayList alInput, EnumFPFunction efpf, boolean pHeaders) {
/* create file name based on input list */
String sFileName1 = "";
String sFileName2 = "";
long partNum1 = 0; // Starting point for addition
long partNum2 = 1; // Starting point for multiplication
for (String sGPN : alInput) {
//remove dashes
sGPN = sGPN.replaceAll("-", "");
partNum1 += Long.parseLong(sGPN, 36); //(base 36)
partNum2 *= Long.parseLong(sGPN, 36); //(base 36)
}
// Initial strings
sFileName1 = "000000000" + Long.toString(partNum1, 36); // base 36
sFileName2 = "000000000" + Long.toString(partNum2, 36); // base 36
// Cropped strings
sFileName1 = sFileName1.substring(sFileName1.length()-9, sFileName1.length());
sFileName2 = sFileName2.substring(sFileName2.length()-9, sFileName2.length());
return sFileName1 + sFileName2;
}

Resources