How to pad strings to a fixed width with NSMutableString? - ios

I'm trying to write a string to a text file. That text file will then be read by another program. That second program is expecting the different "fields" in the text file to be a fixed width. Therefore, when I write the text file with my app, I will need to add spaces between my actual data to get everything to line up correctly. How do I get these spaces added?
So far, I've tried writing a function that takes a source string and a target length as input. If the target is longer than the source, it just appends " ". Code for this routine is below:
- (NSString *) makeStringFrom:(NSString *)source withLength:(NSInteger)length
{
// Method to add spaces to the end of a string to get it to a certain length
if ([source length] > length)
{
// String is too long - throw warning and send it back
NSLog(#"Warning - string is already longer than length supplied. Returning source string");
return source;
}
else if ([source length] == length)
{
// String is already correct length, so just send it back
return source;
}
else
{
// String is too short, need to add spaces
NSMutableString *newString = [[NSMutableString alloc] initWithString:source];
NSLog(#"newString initial length = %d",[newString length]);
for (int current = [source length]; current < length; current ++)
{
[newString stringByAppendingString:#" "];
NSLog(#"hit");
}
NSLog(#"target length = %d. newString length = %d",length,[newString length]);
return newString;
}
}
This apparently doesn't work. The length of the string I'm getting back in the return isn't changing any from the length of the supplied string, even when the NSLog(#"hit"); runs multiple times.

There's a stringByPaddingToLength:withString:startingAtIndex: method on NSString that does just this.

You did a silly mistake here
[newString stringByAppendingString:#" "];
This returns a new string, and it doesnot effect the caller object. You need to store it
newString=[newString stringByAppendingString:#" "];
or simply
[newString appendString:#" "];

You want to change:
[newString stringByAppendingString:#" "];
into:
newString = [newString stringByAppendingString:#" "];

Related

How to decode words in an NSString using a dictionary with keys and values of codes?

I have textual reports that are coded with special shortcuts (i.e #"BLU" for #"BLUE", #"ABV" for #"Above", etc).
I created an NSDictionary where the keys are the coded word and values are the translations.
Currently I translate the string using this code:
NSMutableString *decodedDesc = [#"" mutableCopy];
for (NSString *word in [self.rawDescriprion componentsSeparatedByString:#" "]) {
NSString * decodedWord;
if (word && word.length>0 && [word characterAtIndex:word.length-1] == '.') {
decodedWord = [abbreviations[[word substringToIndex:word.length-1]] stringByAppendingString:#"."];
} else
decodedWord = abbreviations[word];
if (!decodedWord)
decodedWord = word;
[decodedDesc appendString:[NSString stringWithFormat:#"%# ",decodedWord]];
}
_decodedDescription = [decodedDesc copy];
The problem is that the words in the report are not always seperated by a space. Sometimes the are connected to other special characters, such as #"-" or #"/", the code ignores the word because something like #"BLU-ABV" is not in the dictionary keys.
How can I improve this code to ignore special chars while translating the words but preserving them in the translated NSString? For example #"BLU-ABV" would translate into #"Blue-Above".
This can be done by
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:#" -/"];
[self.rawDescription componentsSeparatedByCharactersInSet:characterSet];
where the characterSet contains the Characters you want to separate by or you can even define the CharacterSet with the letters that make out your words (only alphabetical or alphanumeric) and then use the invertedSet.
Use character set to separate it.
NSMutableCharacterSet* cSet = [NSMutableCharacterSet punctuationCharacterSet];
// add your own custom character
[cSet addCharactersInString:#" "];
NSArray *comps = [self.rawDescriprion componentsSeparatedByString:cSet];
You can take a look at which character set is more suitable for your case at Apple Documentation
But I will say it should be punctuation.
I solved it. The solution is to enumerate the original NSString letter by letter. Each letter is added to a word until reaching a special char.
Upon reaching a special char, the translated word, followed by the special char, are dumped into the translated report (which is an NSMutableString) and the word variable is reset to #"".
Here is the code: (not including the dictionary or original NSString initialization)
__block NSString *word;
NSCharacterSet *specialChars = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
[_rawDescriprion enumerateSubstringsInRange:NSMakeRange(0, [_rawDescriprion length])
options:(NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *letter, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (!word)
word = #"";
//if letter is a special char
if ([letter rangeOfCharacterFromSet:specialChars].location != NSNotFound) {
//add old word to decoded string
if (word && abbreviations[word])
[decodedDesc appendString:abbreviations[word]];
else if (word)
[decodedDesc appendString:word];
//Add the punctuation character to the decoded description
[decodedDesc appendString:letter];
//Clear word variable
word = #"";
}
else { //Alpha-numeric letter
//add letter to word
word = [word stringByAppendingString:letter];
}
}];
//add the last word to the decoded string
if (word && abbreviations[word])
[decodedDesc appendString:abbreviations[word]];
else if (word)
[decodedDesc appendString:word];
_decodedDescription = [decodedDesc copy];

Get the unique characters in an NSString

How can I get the unique characters in an NSString?
What I'm trying to do is get all the illegal characters in an NSString so that I can prompt the user which ones were inputted and therefore need to be removed. I start off by defining an NSCharacterSet of legal characters, separate them with every occurrence of a legal character, and join what's left (only illegal ones) into a new NSString. I'm now planning to get the unique characters of the new NSString (as an array, hopefully), but I couldn't find a reference anywhere.
NSCharacterSet *legalCharacterSet = [NSCharacterSet
characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789-()&+:;,'.# "];
NSString *illegalCharactersInTitle = [[self.titleTextField.text.noWhitespace
componentsSeparatedByCharactersInSet:legalCharacterSet]
componentsJoinedByString:#""];
That should help you. I couldn't find any ready to use function for that.
NSMutableSet *uniqueCharacters = [NSMutableSet set];
NSMutableString *uniqueString = [NSMutableString string];
[illegalCharactersInTitle enumerateSubstringsInRange:NSMakeRange(0, illegalCharactersInTitle.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (![uniqueCharacters containsObject:substring]) {
[uniqueCharacters addObject:substring];
[uniqueString appendString:substring];
}
}];
Try with the following adaptation of your code:
// legal set
NSCharacterSet *legalCharacterSet = [NSCharacterSet
characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789-()&+:;,'.# "];
// test strings
NSString *myString = #"LegalStrin()";
//NSString *myString = #"francesco#gmail.com"; illegal string
NSMutableCharacterSet *stringSet = [NSCharacterSet characterSetWithCharactersInString:myString];
// inverts the set
NSCharacterSet *illegalCharacterSet = [legalCharacterSet invertedSet];
// intersection of the string set and the illegal set that modifies the mutable stringset itself
[stringSet formIntersectionWithCharacterSet:illegalCharacterSet];
// prints out the illegal characters with the convenience method
NSLog(#"IllegalStringSet: %#", [self stringForCharacterSet:stringSet]);
I adapted the method to print from another stackoverflow question:
- (NSString*)stringForCharacterSet:(NSCharacterSet*)characterSet
{
NSMutableString *toReturn = [#"" mutableCopy];
unichar unicharBuffer[20];
int index = 0;
for (unichar uc = 0; uc < (0xFFFF); uc ++)
{
if ([characterSet characterIsMember:uc])
{
unicharBuffer[index] = uc;
index ++;
if (index == 20)
{
NSString * characters = [NSString stringWithCharacters:unicharBuffer length:index];
[toReturn appendString:characters];
index = 0;
}
}
}
if (index != 0)
{
NSString * characters = [NSString stringWithCharacters:unicharBuffer length:index];
[toReturn appendString:characters];
}
return toReturn;
}
First of all, you have to be careful about what you consider characters. The API of NSString uses the word characters when talking about what Unicode refers to as UTF-16 code units, but dealing with code units in isolation will not give you what users think of as characters. For example, there are combining characters that compose with the previous character to produce a different glyph. Also, there are surrogate pairs, which only make sense when, um, paired.
As a result, you will actually need to collect substrings which contain what the user thinks of as characters.
I was about to write code very similar to Grzegorz Krukowski's answer. He beat me to it, so I won't but I will add that your code to filter out the legal characters is broken because of the reasons I cite above. For example, if the text contains "é" and it's decomposed as "e" plus a combining acute accent, your code will strip the "e", leaving a dangling combining acute accent. I believe your intent is to treat the "é" as illegal.

I want to print to a new line in the 'terminal' (UITextView) but whenever I hit the done button, it just replaces what's there

This is the method I wrote to add a little 'ConsoleCalc' prefix to each output. The terminal is a UITextView
-(void)writeToTerminal:(NSString *)string {
self.terminal.text = [NSString stringWithFormat:#"ConsoleCalc > %# \n", string];
}
You can see the '\n' that I put to try to make it add a new line. But whenever I enter a new input the TextView just replaces whatever was there with the new result of my input.
I think you're looking for
self.terminal.text = [self.terminal.text stringByAppendingString:[NSString stringWithFormat:#"\nConsoleCalc > %#", string]];
#CodeInOrange's answer is technically correct, but it can be nicely shortened using stringByAppendingFormat: instead of appending [NSString stringWithFormat:]
-(void)writeToTerminal:(NSString *)string
{
[self.teminal setText:[self.teminal.text stringByAppendingFormat:#"ConsoleCalc > %# \n",string]];
}
You have to capture the current text value, then append your new string to the current text:
NSString *currentText = [self.terminal.text copy];
NSString *newLine = [NSString stringWithFormat:#"ConsoleCalc > %# \n",string];
NSString *newText = [NSString stringWithFormat:#"%#\n%#",currentText,newLine];
self.terminal.text = newText;

Pull first name and last initial from string

I have an NSString that contains a users full name. Some names are in the standard first and last formation (Kyle Begeman) and others are just a single name (TechCrunch).
How would I grab the first name as is and then the first initial of the last name, and if there is only one name, just grab the whole name?
Basically I want the above to be turned into Kyle B. or just TechCrunch depending on the name.
NSString *username = #"Kyle Begeman"
NSString *otherUserName = #"TechCrunch"
converted to
#"Kyle B"
// No conversion because it is a single word name
#"TechCrunch"
Using substringToIndex is how I can grab the first letter in the whole string, and I know there is a way to separate the string by #" " whitespace into an array but I can figure out how to easily produce the result the way it needs to be.
Any help would be great!
(NSString*)firstNameWithInitial:(NSString*)userName {
NSArray *array = [userName componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
array = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF != ''"]];
NSString *firstName = [array objectAtIndex:0];
NSString finalNameString;
if ([array count] > 1) {
NSString *lastNameInitial = [[array objectAtIndex:1] substringToIndex:1];
finalNameString = [firstName stringByAppendingString:[NSString stringWithFormat:#" %#", lastNameInitial]];
else {
finalNameString = firstName;
}
return finalNameString;
}
This function should return what you need. Note that you can modify this to work with people who have more than 2 names, by checking the number of objects in the array.
Find a position pos of the first space in the string. If there is no space, or if the space is the last character of the string, then return the entire string; otherwise, return substring in the range from zero to pos+1, inclusive:
NSRange range = [str rangeOfString:#" "];
if (range.location == NSNotFound || range.location == str.length-1) {
return str;
} else {
return [str substringToIndex:range.location+1];
}
You could use NSScanner to find substrings.
NSString *name = #"Some name";
NSString *firstName;
NSString *lastName;
NSScanner *scanner = [NSScanner scannerWithString:name];
[scanner scanUpToString:#" " intoString:&firstName]; // Scan all characters up to the first space
[scanner scanUpToString:#"" intoString:&lastName]; // Scan remaining characters
if (lastName != nil) {
// It was no space and lastName is empty
} else {
// There was at least one space and lastName contains a string
}

iOS: changing NSString value

Will this bit of code produce any memory leaks? Is it the correct way to change NSString values?
NSString * enemiesAndElementsTextureFileName = #"bla bla";
enemiesAndElementsTextureFileName = #"bl";
That way of doing it won't cause any memory leaks and it is indeed correct. In this case you wouldn't need an NSMutableString because you aren't altering the string literal itself, you are simply replacing the string value with a new one (replacing #"bla bla" with #"bl").
In this case, however, your string will now be 'bl', so you can delete that first line value and just have NSString * enemiesAndElementsTextureFileName = #"bl";
Yes NSString allocated once. This is one of the way
Yes, use NSMutableString with the following method as your needs:
// Allocate
NSMutableString *str = [[NSMutableString alloc] initWithCapacity:10];
// set string content
[str setString:#"1234"];
// Append
[str appendString:#"567"];
// Concat
[str appendFormat:#"age is %i and height is %.2f", 27, 1.55f];
// Replace
NSRange range = [str rangeOfString:#"height"];//查找字符串height的位置
[str replaceCharactersInRange:range withString:#"no"];
// Insert
[str insertString:#"abc" atIndex:2];
// Delete
range = [str rangeOfString:#"age"];
[str deleteCharactersInRange:range];
NSLog(#"%#", str);

Resources