Strange return of RangeOfString method - objective-c

Little explanation of the code : I have a dict that looks like this :
HC_1 : test1, HC_2: test2, ....
If I find HC_1 in the "theModifiedReportTemplateContent", I look for the first ">" after and I add "test1".
-(NSMutableString*)almMapData:(NSDictionary *)aDictData;
{
NSMutableString *theModifiedReportTemplateContent = itsReportTemplateContent mutableCopy];
for(id theCell in aDictData)
{
NSRange theNameRange = [theModifiedReportTemplateContent rangeOfString:theCell];
NSString *theNewContent = [theModifiedReportTemplateContent substringFromIndex:theNameRange.location];
if(theNameRange.location != NSNotFound)
{
NSRange theCharRange = [theNewContent rangeOfString:#">"];
long theIndex =((long)theCharRange.location+(long)theNameRange.location+1);
[theModifiedReportTemplateContent insertString:[aDictData objectForKey:theCell] atIndex:theIndex];
}
}
return theModifiedReportTemplateContent;
}
My problem is that we I print theCharRange, I have something like :
location=13, length=1
{
2001-01-01 00:00:00 +0000
<object returned empty description>
}
So I have a exception with InsertString atIndex.

Related

Swift Cannot convert value of type CBPeripheral to expected argument type

The code I have:
func didDiscoverBLE(_ peripheral: CBPeripheral!, address: String!, rssi: Int32) {
DispatchQueue.main.async(execute: {() -> Void in
// Handle Discovery
self.arrayPeripehral.contains(where:peripheral)
return
})
self.arrayPeripehral.append(peripheral)
let title: String = "\(peripheral.name) \(address) (RSSI:\(rssi))"
self.arrayPeripheralName.append(title)
In this line i have a problem:
self.arrayPeripehral.contains(where:peripheral)
return
})
Has anyone an idea?
Here is the code I copied from the obective c to the swift and got stuck on this error
- (void)didDiscoverBLE:(CBPeripheral *)peripheral address:(NSString *)address rssi:(int)rssi
{
dispatch_async(dispatch_get_main_queue(), ^{
// Handle Discovery
if([arrayPeripehral containsObject:peripheral])
return;
[arrayPeripehral addObject:peripheral];
NSString * title = [NSString stringWithFormat:#"%# %# (RSSI:%d)", peripheral.name, address, rssi];
[arrayPeripheralName addObject:title];
Change the type of arrayPeripehral to [CBPeripheral] from [Any] that will give compiler more idea about its type then use contains(where:) like this to check array contains object or not.
var arrayPeripehral = [CBPeripheral]()
Now use contains(where:) this way to check array contains object or not.
if self.arrayPeripehral.contains(where: { $0.name == peripheral.name }) {
return
}
Also change type declaration of arrayPeripheralName to [String] from [Any] as of you are appending only String object in it.
var arrayPeripheralName = [String]()

How to check that two format strings are compatible?

Examples:
"Something %d" and "Something else %d" // Compatible
"Something %d" and "Something else %f" // Not Compatible
"Something %d" and "Something %d else %d" // Not Compatible
"Something %d and %f" and "Something %2$f and %1$d" // Compatible
I figured there should be some C function for this, but I'm not getting any relevant search results. I mean the compiler is checking that the format string and the arguments match, so the code for checking this is already written. The only question is how I can call it.
I'm using Objective-C, so if there is an Objective-C specific solution that's fine too.
Checking if 2 printf() format strings are compatible is an exercise in format parsing.
C, at least, has no standard run-time compare function such as:
int format_cmp(const char *f1, const char *f2); // Does not exist
Formats like "%d %f" and "%i %e" are obviously compatible in that both expect an int and float/double. Note: float are promoted to double as short and signed char are promoted to int.
Formats "%*.*f" and "%i %d %e" are compatible, but not obvious: both expect an int,int and float/double.
Formats "%hhd" and "%d" both expect an int, even though the first will have it values cast to signed char before printing.
Formats "%d" and "%u" are not compatible. Even though many systems will behaved as hoped. Note: Typically char will promote to int.
Formats "%d" and "%ld" are not strictly compatible. On a 32-bit system there are equivalent, but not in general. Of course code can be altered to accommodate this. OTOH "%lf" and "%f" are compatible due to the usual argument promotions of float to double.
Formats "%lu" and "%zu" may be compatible, but that depends on the implementation of unsigned long and size_t. Additions to code could allow this or related equivalences.
Some combinations of modifiers and specifiers are not defined like "%zp". The following does not dis-allow such esoteric combinations - but does compare them.
Modifiers like "$" are extensions to standard C and are not implemented in the following.
The compatibility test for printf() differs from scanf().
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
typedef enum {
type_none,
type_int,
type_unsigned,
type_float,
type_charpointer,
type_voidpointer,
type_intpointer,
type_unknown,
type_type_N = 0xFFFFFF
} type_type;
typedef struct {
const char *format;
int int_queue;
type_type type;
} format_T;
static void format_init(format_T *state, const char *format);
static type_type format_get(format_T *state);
static void format_next(format_T *state);
void format_init(format_T *state, const char *format) {
state->format = format;
state->int_queue = 0;
state->type = type_none;
format_next(state);
}
type_type format_get(format_T *state) {
if (state->int_queue > 0) {
return type_int;
}
return state->type;
}
const char *seek_flag(const char *format) {
while (strchr("-+ #0", *format) != NULL)
format++;
return format;
}
const char *seek_width(const char *format, int *int_queue) {
*int_queue = 0;
if (*format == '*') {
format++;
(*int_queue)++;
} else {
while (isdigit((unsigned char ) *format))
format++;
}
if (*format == '.') {
if (*format == '*') {
format++;
(*int_queue)++;
} else {
while (isdigit((unsigned char ) *format))
format++;
}
}
return format;
}
const char *seek_mod(const char *format, int *mod) {
*mod = 0;
if (format[0] == 'h' && format[1] == 'h') {
format += 2;
} else if (format[0] == 'l' && format[1] == 'l') {
*mod = ('l' << CHAR_BIT) + 'l';
format += 2;
} else if (strchr("ljztL", *format)) {
*mod = *format;
format++;
} else if (strchr("h", *format)) {
format++;
}
return format;
}
const char *seek_specifier(const char *format, int mod, type_type *type) {
if (strchr("di", *format)) {
*type = type_int;
format++;
} else if (strchr("ouxX", *format)) {
*type = type_unsigned;
format++;
} else if (strchr("fFeEgGaA", *format)) {
if (mod == 'l') mod = 0;
*type = type_float;
format++;
} else if (strchr("c", *format)) {
*type = type_int;
format++;
} else if (strchr("s", *format)) {
*type = type_charpointer;
format++;
} else if (strchr("p", *format)) {
*type = type_voidpointer;
format++;
} else if (strchr("n", *format)) {
*type = type_intpointer;
format++;
} else {
*type = type_unknown;
exit(1);
}
*type |= mod << CHAR_BIT; // Bring in modifier
return format;
}
void format_next(format_T *state) {
if (state->int_queue > 0) {
state->int_queue--;
return;
}
while (*state->format) {
if (state->format[0] == '%') {
state->format++;
if (state->format[0] == '%') {
state->format++;
continue;
}
state->format = seek_flag(state->format);
state->format = seek_width(state->format, &state->int_queue);
int mod;
state->format = seek_mod(state->format, &mod);
state->format = seek_specifier(state->format, mod, &state->type);
return;
} else {
state->format++;
}
}
state->type = type_none;
}
// 0 Compatible
// 1 Not Compatible
// 2 Not Comparable
int format_cmp(const char *f1, const char *f2) {
format_T state1;
format_init(&state1, f1);
format_T state2;
format_init(&state2, f2);
while (format_get(&state1) == format_get(&state2)) {
if (format_get(&state1) == type_none)
return 0;
if (format_get(&state1) == type_unknown)
return 2;
format_next(&state1);
format_next(&state2);
}
if (format_get(&state1) == type_unknown)
return 2;
if (format_get(&state2) == type_unknown)
return 2;
return 1;
}
Note: only minimal testing done. Lots of additional considerations could be added.
Known shortcomings: hh,h,l,ll,j,z,t modifiers with n. l with s,c.
[Edit]
OP comments about security concerns. This changes the nature of the post and the compare from an equality one to a security one. I'd imagine that one of the patterns (A) would be a reference pattern and the next (B) would be the test. The test would be "is B at least as secure as A?". Example A = "%.20s" and B1 = "%.19s", B2 = "%.20s", B3 = "%.21s". B1 and B2 both pass the security test as they do not extract more the 20 char. B3 is a problem as it goes pass the reference limit of 20 char. Further any non-width qualified with %s %[ %c is a security problem - in the reference or test pattern. This answer's code does not address this issue.
As mentioned, code does not yet handle modifiers with "%n".
[2018 edit]
Concerning "Formats "%d" and "%u" are not compatible.": This is for values to be printed in general. For values in the [0..INT_MAX] range, either format may work per C11dr ยง6.5.2.2 6.
My understanding of what you want, is that, you basically want a method which can look at two strings and detect if they both have the same types of values in them. Or something a long those lines.... If so, then try this (or something along the lines of this):
-(int)checkCompatible:(NSString *)string_1 :(NSString *)string_2 {
// Separate the string into single elements.
NSArray *stringArray_1 = [string_1 componentsSeparatedByString:#" "];
NSArray *stringArray_2 = [string_2 componentsSeparatedByString:#" "];
// Store only the numbers for comparison in a new array.
NSMutableArray *numbers_1 = [[NSMutableArray alloc] init];
NSMutableArray *numbers_2 = [[NSMutableArray alloc] init];
// Make sure the for loop below, runs for the appropriate
// number of cycles depending on which array is bigger.
int loopMax = 0;
if ([stringArray_1 count] > [stringArray_2 count]) {
loopMax = (int)[stringArray_1 count];
}
else {
loopMax = (int)[stringArray_2 count];
}
// Now go through the stringArray's and store only the
// numbers in the mutable array's. This will be used
// during the comparison stage.
for (int loop = 0; loop < loopMax; loop++) {
NSCharacterSet *notDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
if (loop < [stringArray_1 count]) {
if ([[stringArray_1 objectAtindex:loop] rangeOfCharacterFromSet:notDigits].location == NSNotFound) {
// String consists only of the digits 0 through 9.
[numbers_1 addObject:[stringArray_1 objectAtindex:loop]];
}
}
if (loop < [stringArray_2 count]) {
if ([[stringArray_2 objectAtindex:loop] rangeOfCharacterFromSet:notDigits].location == NSNotFound) {
// String consists only of the digits 0 through 9.
[numbers_2 addObject:[stringArray_2 objectAtindex:loop]];
}
}
}
// Now look through the mutable array's
// and perform the type comparison,.
if ([numbers_1 count] != [numbers_2 count]) {
// One of the two strings has more numbers
// than the other, so they are NOT compatible.
return 1;
}
else {
// Both string have the same number of numbers
// numbers so lets go through them to make
// sure the numbers are of the same type.
for (int loop = 0; loop < [numbers_1 count]; loop++) {
// Check to see if the number in the current array index
// is a float or an integer. All the numbers in the array have
// to be the SAME type, in order for the strings to be compatible.
BOOL check_float_1 = [[NSScanner scannerWithString:[numbers_1 objectAtindex:loop]] scanFloat:nil];
BOOL check_int_1 = [[NSScanner scannerWithString:[numbers_1 objectAtindex:loop]] scanInt:nil];
BOOL check_float_2 = [[NSScanner scannerWithString:[numbers_2 objectAtindex:loop]] scanFloat:nil];
BOOL check_int_2 = [[NSScanner scannerWithString:[numbers_2 objectAtindex:loop]] scanInt:nil];
if (check_float_1 == YES) {
if (check_float_2 == NO) {
return 1;
}
}
else if (check_int_1 == YES) {
if (check_int_2 == NO) {
return 1;
}
}
else {
// Error of some sort......
return 1;
}
}
// All the numbers in the strings are of the same
// type (otherwise we would NOT have reached
// this point). Therefore the strings are compatible.
return 0;
}
}

stringByReplacingOccurrencesOfString is not working?

I am trying to translate this Objective-C code :
if ([[self.appStoreCountry stringByReplacingOccurrencesOfString:#"[A-Za-z]{2}" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, 2)] length])
So I tried to wrote this code :
if !self.appStoreCountry.stringByReplacingOccurrencesOfString("[A-Za-z]{2}", withString: "", options: NSStringCompareOptions.RegularExpressionSearch, range: Range(start: 0, end: 2)).isEmpty
The problem is that the stringByReplacingOccurrencesOfString part does not seem to return a string. I have the error message from xcode : Cannot convert the expression's type 'Bool' to type 'String'.
I investigated a little more and tried :
let replaced = self.appStoreCountry.stringByReplacingOccurrencesOfString("[A-Za-z]{2}", withString: "", options: NSStringCompareOptions.RegularExpressionSearch, range: Range(start: 0, end: 2))
The error message is similar but even more weird : Cannot convert the expression's type 'String' to type 'String'.
Am I doing something wrong or is there a bug I should submit to Apple?
Keep in mind that Foundation's types are different from Swift's types. A String in Swift is not an NSString, and a Range is not an NSRange. This does work:
let country = self.appStoreCountry // if self.appStoreCountry is an NSString
let country: NSString = self.appStoreCountry // if self.appStoreCountry is a Swift string
let replaced = country.stringByReplacingOccurrencesOfString("[A-Za-z]{2}", withString: "", options: .RegularExpressionSearch, range: NSMakeRange(0, 2))
Also note the short-hand notation for .RegularExpressionSearch, which makes using enums in Swift a bit easier.
extension String{
func exclude(find:String) -> String {
return stringByReplacingOccurrencesOfString(find, withString: "", options: .LiteralSearch, range: nil)
}
func replaceAll(find:String, with:String) -> String {
return stringByReplacingOccurrencesOfString(find, withString: with, options: .LiteralSearch, range: nil)
}
}
"1-2-3-4-5-6-7-8-9-0".exclude("-") // "1234567890"
"1-2-3-4-5-6-7-8-9-0".replaceAll("-", with: "+") // "1+2+3+4+5+6+7+8+9+0"
Try This code:
var originalString="Hello World"
var needToReplaceString = "Hello"
var replaceString = " Hi"
var newString = originalString.stringByReplacingOccurrencesOfString(needToReplaceString, withString: replaceString, options: nil, range: nil)
println(newString)
Open a Playground and add this code, now it's correct:
var str = NSString(CString: "en")
str = str.stringByReplacingOccurrencesOfString("[A-Za-z]{2}", withString: "", options: NSStringCompareOptions.RegularExpressionSearch, range: NSRange(location: 0, length: 2))
if str == ""
{
var a = "true"
} else {
var b = "false"
}
Your problems were:
.isEmpty is not a method of NSString
You were using Range() and not NSRange as required by the method .stringByReplacingOccurrencesOfString of NSString
since your replace returns an empty string and not a nil object, you have to test for "" in the if statement
let maskCharSet = NSCharacterSet(charactersInString: " ()-.")
let cleanedString = phoneNumber.componentsSeparatedByCharactersInSet(maskCharSet).reduce("", combine: +)

Conditional Statement checking length of string

I'm trying to create a conditional statement that returns to me "nothing entered" if the string length is less than 1. However, I'm getting an error message in xcode that says: Expected identifier. I think my syntax may be wrong but I can't figure it out.
- (IBAction)batman:(id)sender {
if ([self.nameTextField.text.length] < 1)
{ //returnType method argument
[NSString stringWithFormat:#"nothing entered."];
}
else {
self.secondLabel.text = [NSString stringWithFormat:#"Batman %#", self.nameTextField.text ];
}
}
Just get rid of your brackets [] and it should work:
- (IBAction)batman:(id)sender {
if (self.nameTextField.text.length < 1) {
self.secondLabel.text = #"nothing entered.";
} else {
self.secondLabel.text = [NSString stringWithFormat:#"Batman %#", self.nameTextField.text ];
}
}

How can I simplify my nested for-loops with ReactiveCocoa?

Say I have 2 NSDictionaries that I don't know beforehand like:
NSDictionary *dictA = #{ #"key1" : #1,
#"key2" : #2 };
NSDictionary *dictB = #{ #"key1" : #"a string" };
I want to find the first match between the keys of dictB and the keys or values of dictA. Each key of dictB will either be a NSNumber or a string. If it's a number, try to find a match from the values of dictA. If it's a string, try to find a match from the keys of dictA.
Using for loops, it would looks something like this:
id match;
for (id key in dictA ) {
for (id _key in dictB {
if ( [_key is kindOfClass:NSNumber.class] && _key == dictA[key] ) {
match = _key
goto outer;
}
else if ( [_key is kindOfClass:NSString.class] && [_key isEqualToString:key] ) {
match = _key
goto outer;
}
}
};
outer:;
NSString *message = match ? #"A match was found" : #"No match was found";
NSLog(message);
How could I rewrite this with ReactiveCocoa using RACSequence and RACStream methods so it looks something like:
// shortened pseudo code:
// id match = [dictA.rac_sequence compare with dictB.rac_sequence using block and return first match];
You basically would like to create the cartesian product of the dictionaries and make a selection on it. There is no default operator in ReactiveCocoa that I know of that would do this for you. (In LINQ there are operators for this.) In RAC the simplest solution is to use the scanWithStart:combine: method to implement this operation. Once the cartesian is ready, the filter: and take:1 operations will produce the sequence of your choice.
NSDictionary *adic = #{#"aa":#"vb", #"ab": #"va"};
NSDictionary *bdic = #{#"ba": #"va", #"bb":#"vb"};;
RACSequence *aseq = adic.rac_keySequence;
RACSequence *bseq = bdic.rac_keySequence;
RACSequence *cartesian = [[aseq scanWithStart:nil combine:^id(id running, id next_a) {
return [bseq scanWithStart:nil combine:^id(id running, id next_b) {
return RACTuplePack(next_a, next_b);
}];
}] flatten];
RACSequence *filteredCartesian = [cartesian filter:^BOOL(RACTuple *value) {
RACTupleUnpack(NSString *key_a, NSString *key_b) = value;
// business logic with keys
return false;
}];
RACSequence *firstMatch = [filteredCartesian take:1];