如何解决在我的macOS应用程序中,我正在使用UserDefaults dictionaryRepresentation有时我会得到编码未知的字符串有什么建议吗?
我正在使用Objective-C应用程序,具体地说,我正在使用以下代码收集NSUserDefaults的字典表示形式:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *userDefaultsDict = [defaults dictionaryRepresentation];
在枚举结果字典的键和对象时,有时我会发现一种不透明的字符串,您可以在下图中看到它:
所以这似乎是编码问题。
如果我尝试打印字符串描述,则调试器将正确打印:
Printing description of obj:
tsuqsx
但是,如果我尝试将obj写入文件或以其他方式使用它,则会得到如下这样的不可读输出:
我想实现以下目标:
-
以某种方式检测字符串是否存在编码问题。
-
将字符串转换为UTF8编码,以在程序的其余部分中使用它。
任何帮助将不胜感激。谢谢
编辑:可能会出现非常棘手的解决方案,可帮助解释我正在尝试做的事情。
在尝试了所有可能的基于dataUsingEncoding的解决方案并返回之后,我得出了绝对奇怪的以下解决方案,但我将其发布在这里,希望它可以帮助某人猜测编码以及如何处理不可打印的字符:
- (BOOL)isProblematicString:(Nsstring *)candidateString {
BOOL returnValue = YES;
if ([candidateString length] <= 2) {
return NO;
}
const char *temp = [candidateString UTF8String];
long length = temp[0];
char *dest = malloc(length + 1);
long ctr = 1;
long usefulCounter = 0;
for (ctr = 1;ctr <= length;ctr++) {
if ((ctr - 1) % 3 == 0) {
memcpy(&dest[ctr - usefulCounter - 1],&temp[ctr],1);
} else {
if (ctr != 1 && ctr < [candidateString length]) {
if (temp[ctr] < 0x10 || temp[ctr] > 0x1F) {
returnValue = NO;
}
}
usefulCounter += 1;
}
}
memset(&dest[length],1);
free(dest);
return returnValue;
}
- (Nsstring *)utf8StringFromUnkNownEncodedString:(Nsstring*)originalUnkNownString {
const char *temp = [originalUnkNownString UTF8String];
long length = temp[0];
char *dest = malloc(length + 1);
long ctr = 1;
long usefulCounter = 0;
for (ctr = 1;ctr <= length;ctr++) {
if ((ctr - 1) % 3 == 0) {
memcpy(&dest[ctr - usefulCounter - 1],1);
} else {
usefulCounter += 1;
}
}
memset(&dest[length],1);
Nsstring *returnValue = [[Nsstring alloc] initWithUTF8String:dest];
free(dest);
return returnValue;
}
这将为我返回一个字符串,可用于构建完整的UTF8字符串。我正在寻找一个干净的解决方案。任何帮助是极大的赞赏。谢谢
解决方法
我们正在谈论的是来自/Library/Preferences/.GlobalPreferences.plist
的字符串
(键com.apple.preferences.timezone.new.selected_city
)。
NSString *city = [[NSUserDefaults standardUserDefaults]
stringForKey:@"com.apple.preferences.timezone.new.selected_city"];
NSLog(@"%@",city); // \^Zt\^\\^]s\^]\^\u\^V\^_q\^]\^[s\^W\^Zx\^P
(lldb) p [city description]
(__NSCFString *) $1 = 0x0000600003f6c240 @"\x1at\x1c\x1ds\x1d\x1cu\x16\x1fq\x1d\x1bs\x17\x1ax\x10"
我想实现以下目标:
- 以某种方式检测字符串是否存在编码问题。
- 将字符串转换为UTF8编码,以便在程序的其余部分中使用它。
&
尝试了所有可能的基于dataUsingEncoding的解决方案并返回后。
此字符串没有编码问题,并且\x1a
,\x1c
等字符是有效字符。
您可以使用ASCII,UTF-8等调用dataUsingEncoding:
,但是所有这些字符仍然是
当下。它们称为control characters(或非印刷字符)。链接的Wikipedia页面解释了这些字符是什么以及如何用ASCII,扩展ASCII和unicode定义它们。
您正在寻找的是一种从字符串中删除控制字符的方法。
删除控制字符
我们可以为新方法创建一个类别:
@interface NSString (ControlCharacters)
- (NSString *)stringByRemovingControlCharacters;
@end
@implementation NSString (ControlCharacters)
- (NSString *)stringByRemovingControlCharacters {
// TODO Remove control characters
return self;
}
@end
在下面的所有示例中,city
变量都是通过这种方式创建的...
NSString *city = [[NSUserDefaults standardUserDefaults]
stringForKey:@"com.apple.preferences.timezone.new.selected_city"];
...,其中包含@"\x1at\x1c\x1ds\x1d\x1cu\x16\x1fq\x1d\x1bs\x17\x1ax\x10"
。也全部
以下示例通过以下代码进行了测试:
NSString *cityWithoutCC = [city stringByRemovingControlCharacters];
// tsuqsx
NSLog(@"%@",cityWithoutCC);
// {length = 6,bytes = 0x747375717378}
NSLog(@"%@",[cityWithoutCC dataUsingEncoding:NSUTF8StringEncoding]);
拆分并加入
一种方法是利用NSCharacterSet.controlCharacterSet
。
有一个stringByTrimmingCharactersInSet:
方法(NSString
),但只会从开头/结尾删除这些字符,
这不是您想要的。您可以使用一个技巧:
- (NSString *)stringByRemovingControlCharacters {
NSArray<NSString *> *components = [self componentsSeparatedByCharactersInSet:NSCharacterSet.controlCharacterSet];
return [components componentsJoinedByString:@""];
}
它将控制字符分割为字符串,然后将这些组件重新连接在一起。不是一种非常有效的方法,但是它可以工作。
ICU转换
另一种方法是使用ICU转换(请参见ICU User Guide)。
有一个stringByApplyingTransform:reverse:
方法(NSString
),但它仅接受预定义的常量。文档说:
由
NSStringTransform
类型定义的常量提供了基础ICU转换功能提供的功能的子集。要应用《 ICU用户指南》中定义的没有相应的NSStringTransform
常量的ICU转换,请创建NSMutableString
的实例,然后调用applyTransform:reverse:range:updatedRange:
方法。
让我们更新实现:
- (NSString *)stringByRemovingControlCharacters {
NSMutableString *result = [self mutableCopy];
[result applyTransform:@"[[:Cc:] [:Cf:]] Remove"
reverse:NO
range:NSMakeRange(0,self.length)
updatedRange:nil];
return result;
}
[:Cc:]
代表控制字符,[:Cf:]
代表格式字符。两者都代表与已经提到的NSCharacterSet.controlCharacterSet
相同的字符集。文档:
包含Unicode通用类别Cc和Cf中的字符的字符集。
遍历字符
NSCharacterSet
还提供了characterIsMember:
方法。在这里,我们需要遍历字符(unichar
)并检查它是否是控制字符。
让我们更新实现:
- (NSString *)stringByRemovingControlCharacters {
if (self.length == 0) {
return self;
}
NSUInteger length = self.length;
unichar characters[length];
[self getCharacters:characters];
NSUInteger resultLength = 0;
unichar result[length];
NSCharacterSet *controlCharacterSet = NSCharacterSet.controlCharacterSet;
for (NSUInteger i = 0 ; i < length ; i++) {
if ([controlCharacterSet characterIsMember:characters[i]] == NO) {
result[resultLength++] = characters[i];
}
}
return [NSString stringWithCharacters:result length:resultLength];
}
在这里,我们过滤掉属于unichar
的所有字符(controlCharacterSet
)。
其他方式
还有其他方法可以遍历字符-例如-Most efficient way to iterate over all the chars in an NSString。
BBEdit和其他人
让我们将此字符串写入文件:
NSString *city = [[NSUserDefaults standardUserDefaults]
stringForKey:@"com.apple.preferences.timezone.new.selected_city"];
[city writeToFile:@"/Users/zrzka/city.txt"
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
如何处理/显示所有这些控件字符取决于编辑器。这是一个示例-Visual Studio代码。
查看-禁用控制字符:
视图-启用控制字符:
BBEdit显示问号(上下颠倒),但是我敢肯定有一种方法可以 切换控制字符渲染。没有安装BBEdit进行验证。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。