Trimming NSString with multiple conditions - iphone

I'm having a hard time with trimming some characters from a NSString. Given an existing text view with text in it, the requirements are:
Trim leading spaces and newlines (Basically ignore any leading whitespace and newlines)
Copy up to max of 48 chars into the new string OR until a newline is encountered.
I have found that I could do the first requirement from another SO question here with the code:
NSRange range = [textView.text rangeOfString:#"^\\s*" options:NSRegularExpressionSearch];
NSString *result = [textView.text stringByReplacingCharactersInRange:range withString:#""];
However, I'm having trouble with doing the 2nd requirement. Thank you!

This will do what your looking to do. Also it is an easier way to trim leading whitespace and newlines.
NSString *text = textView.text;
//remove any leading or trailing whitespace or line breaks
text = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//find the the range of the first occuring line break, if any.
NSRange range = [text rangeOfString:#"\n"];
//if there is a line break, get a substring up to that line break
if(range.location != NSNotFound)
text = [text substringToIndex:range.location];
//else if the string is larger than 48 characters, trim it
else if(text.length > 48)
text = [text substringToIndex:48];

This should work. Basically what it does is it loops through the characters in the text of the textview and checks if the character it's currently on is a newline character. It also checks if it has reached 48 characters yet. If the character is not a new line character and it has not reached 48 characters yet, then it adds the character to a result string:
NSString *resultString = [NSString string];
NSString *inputString = textView.text;
for(int currentCharacterIndex = 0; currentCharacterIndex < inputString.length; currentCharacterIndex++) {
unichar currentCharacter = [inputString characterAtIndex:currentCharacterIndex];
BOOL isLessThan48 = resultString.length < 48;
BOOL isNewLine = (currentCharacter == '\n');
//If the character isn't a new line and the the result string is less then 48 chars
if(!isNewLine && isLessThan48) {
//Adds the current character to the result string
resultString = [resultString stringByAppendingFormat:[NSString stringWithFormat:#"%C", currentCharacter]];
}
//If we've hit a new line or the string is 48 chars long, break out of the loop
else {
break;
}
}

Related

Detecting if a character is either a letter or a number

In objective-c, how would I check if a single character was either a letter or a number? I would like to eliminate all other characters.
To eliminate non letters:
NSString *letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSCharacterSet *notLetters = [[NSCharacterSet characterSetWithCharactersInString:letters] invertedSet];
NSString *newString = [[string componentsSeparatedByCharactersInSet:notLetters] componentsJoinedByString:#""];
To check one character at a time:
for (int i = 0; i < [string length]; i++) {
unichar c = [string characterAtIndex:i];
if ([notLetters characterIsMember:c]) {
...
}
}
NSCharacterSet *validChars = [NSCharacterSet alphanumericCharacterSet];
NSCharacterSet *invalidChars = [validChars invertedSet];
NSString *targetString = [[NSString alloc] initWithString: #"..."];
NSArray *components = [targetString componentsSeparatedByCharactersInSet:invalidChars];
NSString *resultString = [components componentsJoinedByString:#""];
Many ways to do it, here is one using character sets:
unichar ch = '5';
BOOL isLetter = [[NSCharacterSet letterCharacterSet] characterIsMember: ch];
BOOL isDigit = [[NSCharacterSet decimalDigitCharacterSet] characterIsMember: ch];
NSLog(#"'%C' is a letter: %d or a digit %d", ch, isLetter, isDigit);
You can use the C functions declared in ctype.h (included by default with Foundation). Be careful with multibyte characters though. Check the man pages.
char c = 'a';
if (isdigit(c)) {
/* ... */
} else if (isalpha(c)) {
/* ... */
}
/* or */
if (isalnum(c))
/* ... */
Checking if a character is a (arabic) number:
NSString* text = [...];
unichar character = [text characterAtIndex:0];
BOOL isNumber = (character >= '0' && character <= '9');
It would be different if you would to know other numeric characters (indian numbers, japanese numbers etc.)
Whether character is a letter depends on what you mean by letter. ASCII letters? Unicode letters?
I'm not too familiar with obj-c, but this sounds like something that can be achieved using a regex pattern.
I did some searching and found this:
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSRegularExpression_Class/Reference/Reference.html
Try running a regex of "[0-9]" on each character to determine if it is a number.
Save the number to a temporary array, your array should end up with only the numbers.
I would type out the code...but as I said before, I'm not familiar with obj-c. I hope this helps though.
You can check them by comparing the ASCII value for number (0-9 ASCII ranged from 48-57) .. Also you can use NSScanner in Objective C to test for int or a char.

How to check whether a string contains white spaces

How to check whether a string contains whitespaces in between characters?
use rangeOfCharactersFromSet:
NSString *foo = #"HALLO WELT";
NSRange whiteSpaceRange = [foo rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
if (whiteSpaceRange.location != NSNotFound) {
NSLog(#"Found whitespace");
}
note: this will also find whitespace at the beginning or end of the string. If you don't want this trim the string first...
NSString *trimmedString = [foo stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSRange whiteSpaceRange = [trimmedString rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
You can also follow these steps:
NSArray *componentsSeparatedByWhiteSpace = [testString componentsSeparatedByString:#" "];
If there is any whitespace in your string, then it will separate those and store different components in the array. Now you need to take the count of array. If count is greater than 1, it means there are two components, i.e, presence of white space.
if([componentsSeparatedByWhiteSpace count] > 1){
NSLog(#"Found whitespace");
}

How to know the length of NSString that fits a UILabel with fixed size?

I know NSString has methods that determine the frame size for it, using NSString UIKit Additions, sizeWithFont......
How about the other way around? I mean if I have a fixed frame size, how do I know how many characters or words for a NSString that can fit into it?
If I know this, I can cut off the NSString easily.
thanks
It might not be the most elegant solution, but you could do something like this:
- (NSString *)string:(NSString *)sourceString reducedToWidth:(CGFloat)width withFont:(UIFont *)font {
if ([sourceString sizeWithFont:font].width <= width)
return sourceString;
NSMutableString *string = [NSMutableString string];
for (NSInteger i = 0; i < [sourceString length]; i++) {
[string appendString:[sourceString substringWithRange:NSMakeRange(i, 1)]];
if ([string sizeWithFont:font].width > width) {
if ([string length] == 1)
return nil;
[string deleteCharactersInRange:NSMakeRange(i, 1)];
break;
}
}
return string;
}
Then call it like this:
NSString *test = #"Hello, World!";
CGFloat width = 40.0;
UIFont *font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
NSString *reducedString = [self string:test reducedToWidth:width withFont:font];
NSLog(#"%#", reducedString);
You cannot know/determine the number of characters that fits in a UILabel with fixed width because some characters are smaler than others, eg l and m.
There are two options:
Use Mono-Space-Fonts (each character has also a fixed width). Then determine the width for one char in your font with your font-size and calculate the number of chars
Allow any number of characters and check on insert if the inserted characters fit.
You have to know what behaviour you want to have. What should happen if there is text that does not fit. If you only want to truncate (like the solution of mortenfast does) then just use UILineBreakModeTailTruncation for the lineBreakMode-property of your UILabel (there are more options, like TruncateHead, Clip, Word Wrap)
Or you just just use the lineBreak property and set it to NSLineBreakByCharWrapping and move on with your life. https://stackoverflow.com/a/29088337/951349
Thanks #Morten. I've updated the sample code to handle word separation. It also eliminates extra spaces in between words. It has not been tested in the field, but my tests have, thus far, proven OK. Please update at your leisure if you find improvements or bug/glitch fixes.
-(NSString*)string:(NSString*)sourceString reducedToWidth:(CGFloat)width withFont:(UIFont*)font {
// if full string is within bounds, simply return the full string
if( [sourceString sizeWithFont:font].width <= width ) return sourceString;
// break up string into words. if <= 1 word, return original string
NSArray* words = [sourceString componentsSeparatedByString:#" "];
NSInteger numWords = [words count];
if( numWords <= 1 ) return sourceString;
// our return var. we populate as we go
NSMutableString* str = [NSMutableString string];
// temp var to test with before adding to return string
NSMutableString* strTemp = [NSMutableString string];
// string to hold word LESS spaces
NSString* strWordTemp = nil;
// the word we're currently on
NSInteger numWord = 0;
// whether we need to add a space (when not last word)
Boolean addSpace = NO;
// loop through our words....
for( NSString* strWord in words ) {
// which word we're on
numWord++;
// eliminate white space
strWordTemp = [strWord stringByReplacingOccurrencesOfString:#" " withString:#""];
// if this word is empty or was a space(s), skip it
if( [strWordTemp isEqualToString:#""] ) continue;
// append to temp string
[strTemp appendString:strWordTemp];
// if we're still within the bounds...
if( [strTemp sizeWithFont:font].width <= width ) {
// default = no extra space
addSpace = NO;
// if we're not the last word, add a space & check for length
if( numWord < numWords ) {
[strTemp appendString:#" "];
// if adding space made it too long, then just don't add it!
if( [strTemp sizeWithFont:font].width > width ) {
// it was too long with space, so we'll just add word
[str appendString:strWordTemp];
break;
}
// otherwise, it's OK to add the space
else addSpace = YES;
}
// append to return string and continue
[str appendFormat:#"%#%#", strWordTemp, ( addSpace ? #" " : #"" )];
}
// otherwise, we're done
else break;
}
// return our result
return str;
}

Checking for multiple characters in nsstring

i have a string and i want to check for the multiple characters in this string the following code i working fine for one character how to check for the multiple characters.
NSString *yourString = #"ABCCDEDRFFED"; // For example
NSScanner *scanner = [NSScanner scannerWithString:yourString];
NSCharacterSet *charactersToCount = #"C" // For example
NSString *charactersFromString;
if (!([scanner scanCharactersFromSet:charactersToCount intoString:&charactersFromString])) {
// No characters found
NSLog(#"No characters found");
}
NSInteger characterCount = [charactersFromString length];
UPDATE: The previous example was broken, as NSScanner should not be used like that. Here's a much more straight-forward example:
NSString* string = #"ABCCDEDRFFED";
NSCharacterSet* characters = [NSCharacterSet characterSetWithCharactersInString:#"ABC"];
NSUInteger characterCount;
NSUInteger i;
for (i = 0; i < [yourString length]; i++) {
unichar character = [yourString characterAtIndex:i];
if ([characters characterIsMember:character]) characterCount++;
}
NSLog(#"Total characters = %d", characterCount);
Have a look at the following method in NSCharacterSet:
+ (id)characterSetWithCharactersInString:(NSString *)aString
You can create a character set with more than one character (hence the name character set), by using that class method to create your set. The parameter is a string, every character in that string will end up in the character set.
Also look up NSCountedSet. It can help you keep count of multiple instances of the same character.
For example, from the docs:
countForObject:
Returns the count associated with a given object in the receiver.
- (NSUInteger)countForObject:(id)anObject
Parameters
anObject
The object for which to return the count.
Return Value
The count associated with anObject in the receiver, which can be thought of as the number of occurrences of anObject present in the receiver.

Remove newline character from first line of NSString

How can I remove the first \n character from an NSString?
Edit: Just to clarify, what I would like to do is:
If the first line of the string contains a \n character, delete it else do nothing.
ie: If the string is like this:
#"\nhello, this is the first line\nthis is the second line"
and opposed to a string that does not contain a newline in the first line:
#"hello, this is the first line\nthis is the second line."
I hope that makes it more clear.
[string stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]
will trim your string from any kind of newlines, if that's what you want.
[string stringByReplacingOccurrencesOfString:#"\n" withString:#"" options:0 range:NSMakeRange(0, 1)]
will do exactly what you ask and remove newline if it's the first character in the string
This should do the trick:
NSString * ReplaceFirstNewLine(NSString * original)
{
NSMutableString * newString = [NSMutableString stringWithString:original];
NSRange foundRange = [original rangeOfString:#"\n"];
if (foundRange.location != NSNotFound)
{
[newString replaceCharactersInRange:foundRange
withString:#""];
}
return [[newString retain] autorelease];
}
Rather than creating an NSMutableString and using a few retain/release calls, you can use only the original string and simplify the code by using the following instead: (requires 10.5+)
NSRange foundRange = [original rangeOfString:#"\n"];
if (foundRange.location != NSNotFound)
[original stringByReplacingOccurrencesOfString:#"\n"
withString:#""
options:0
range:foundRange];
(See -stringByReplacingOccurrencesOfString:withString:options:range: for details.)
The result of the last call method call can even be safely assigned back to original IF you autorelease what's there first so you don't leak the memory.