V-X*_*eme 4 iphone objective-c nsstring ios
我有一些NSString喜欢:
test = @"this is %25test%25 string";
Run Code Online (Sandbox Code Playgroud)
我试图用一些阿拉伯语文本替换测试,但它并没有完全取代它:
[test stringByReplacingOccurrencesOfString:@"test" withString:@"??????"];
Run Code Online (Sandbox Code Playgroud)
结果是:
this is %25 ?????? %25 string
Run Code Online (Sandbox Code Playgroud)
我读到的一些地方可能存在编码或文本对齐方面的问题.是否需要对阿拉伯字符串操作进行额外的调整.
编辑:我已经使用了NSMutable字符串插入属性,但结果仍然相同.
编辑2:
另一件事发生在我这个特定的例子中造成了大部分麻烦.您上面有一个部分百分比编码的字符串.你有空格,但你也有%25.你应该避免这样做.百分比编码字符串或不编码.必要时(使用stringByAddingPercentEscapesUsingEncoding:)立即转换所有内容.不要试图"硬编码"百分比编码.如果您只是使用"这是一个%اختبار%字符串"(然后在结尾处对整个事物进行百分比编码),那么所有的方向性问题都会消失(看看它如何渲染得很好?).当你真正需要处理方向性时,其余的这些答案解决了更普遍的问题.
编辑:
该行之后的原始答案与人类可读的字符串相关,并且对于人类可读的字符串是正确的,但您的实际问题(基于您的后续跟踪)是关于URL的.URL不是人类可读的字符串,即使它们偶尔看起来像它们.它们是一系列字节,与它们呈现给人类的方式无关."اختبار"不能位于URL的路径或片段部分.这些字符不属于这些部分的合法字符集(允许اختبار成为主机的一部分,但您必须遵循IDN规则).
正确的URL编码this is a %25<arabic>%25 string是:
this%20is%20a%20%2525%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%2525%20string
Run Code Online (Sandbox Code Playgroud)
如果您将此字符串解码并渲染到屏幕,它将显示如下:
this is a %25??????%25 string
Run Code Online (Sandbox Code Playgroud)
但它实际上就是你所指的字符串(它是你应该传递给浏览器的字符串).按照字节(就像计算机一样):
this - this (ALPHA)
%20 - <space> (encoded)
is - is (ALPHA)
%20 - <space> (encoded)
a - a (ALPHA)
%20 - <space> (encoded)
%25 - % (encoded)
25 - 25 (DIGIT)
%D8%A7 - ? (encoded)
%D8%AE - ? (encoded)
%D8%AA - ? (encoded)
%D8%A8 - ? (encoded)
%D8%A7 - ? (encoded)
%D8%B1 - ? (encoded)
%25 - % (encoded)
25 - 25 (DIGIT)
%20 - <space> (encoded)
string - string (ALPHA)
Run Code Online (Sandbox Code Playgroud)
Unicode BIDI显示算法正在做它意味着要做的事情; 它不是你所期望的.但那些是字节,它们的顺序正确.如果您向此字符串添加任何其他字节(如LRO),那么您正在修改URL,这意味着不同的东西.
所以你需要回答的问题是,你是在创建一个URL,还是在制作一个人类可读的字符串?如果你正在制作一个URL,它应该是URL编码的,在这种情况下你不会有这个显示问题(除非这是主机的一部分,这是一套不同的规则,但我不相信这是你的问题).如果这是一个人类可读的字符串,请参阅下面有关如何为BIDI算法提供提示和覆盖的信息.
您可能真的需要两者(人性化的字符串和可以粘贴的正确URL).没关系,你只需要自己处理剪贴板.显示字符串,但是当用户去复制它时,使用UIPasteboard或覆盖将其替换为完全编码的URL copy:.请参见复制,剪切和粘贴操作.这是相当普遍的(请注意在Safari中,它在地址栏中只显示"stackoverflow.com",但如果您复制并粘贴它,它会粘贴" https://stackoverflow.com/ "同样的事情.
原始答案与人类可读的字符串有关.
信不信由你,stringByReplacingOccuranceOfString:正在做正确的事.它只是没有显示你期望的方式.如果你走了characterAtIndex:,你会发现它是:
% 2 5 ? ...
Run Code Online (Sandbox Code Playgroud)
问题是布局引擎在所有"中性方向"字符周围变得非常混乱.引擎无法理解您是否将"%25"连接到左侧到右侧部分或从右到左部分.你必须通过给它一些明确的方向字符来帮助它.
有几种方法可以解决这个问题.首先,您可以使用显式定向隔离的Unicode 6.3 tr9-29方式.这正是Isolates要解决的问题.你有一些文本,你想要的方向完全独立于所有其他文本.我认为iOS或OS X实际上并不支持Unicode 6.3,但对于许多(尽管不是全部)用途,它"有效".
你想用FSI(FIRST STRONG ISOLATE U + 2068)和PDI(POP DIRECTIONAL ISOLATE U + 2069)包围您的阿拉伯语.您也可以使用RLI(RIGHT-TO-LEFT ISOLATE)来显式化.FSI意味着"将此文本视为您找到的第一个强大角色的方向".
所以你可以理想地做到这一点:
NSString *test = @"this is a %25\u2068test\u2069%25 string";
NSString *arabic = @"??????";
NSString *result = [test stringByReplacingOccurrencesOfString:@"test" withString:arabic];
Run Code Online (Sandbox Code Playgroud)
如果您事先知道要替换的内容(那么您知道将FSI和PDI放在何处),那就行了.如果你不这样做,你可以用另一种方式做,并使其成为替代的一部分:
NSString * const FSI = @"\u2068";
NSString * const PDI = @"\u2069";
NSString *test = @"this is %25test%25 string";
NSString *arabic = @"??????";
NSString *replaceString = [@[FSI, arabic, PDI] componentsJoinedByString:@""];
NSString *result = [test stringByReplacingOccurrencesOfString:@"test" withString:replaceString];
Run Code Online (Sandbox Code Playgroud)
我说这"大多数"都有效.这很好UILabel,对于使用Core Text的任何东西都可能没问题.但在NSLog输出中,您将获得这些额外的"占位符"字符:

你也可以去其他地方.我没有检查UIWebView过.
所以还有其他一些选择.您可以使用方向标记.不过,这有点尴尬.LRM和RLM是零宽度强方向字符.所以你可以用LRM(左到右标记)括起阿拉伯语,这样阿拉伯语就不会打扰周围的文字.这有点难看,因为它意味着替代必须意识到它的替代品(这就是为什么隔离物被发明).
NSString * const LRM = @"\u200e";
NSString *test = @"this is a %25test%25 string";
NSString *replaceString = [@[LRM, arabic, LRM] componentsJoinedByString:@""];
NSString *result = [test stringByReplacingOccurrencesOfString:@"test" withString:replaceString];
Run Code Online (Sandbox Code Playgroud)
BTW,Directional Marks通常是正确的答案.它们应该始终是您尝试的第一件事.这个特殊问题有点太棘手了.
另一种方法是使用Explicit Directional Overrides.这些是巨大的"做我告诉你要做的事"的核心世界.你应该尽可能避免它们.它们存在一些安全问题,使得它们在某些地方被禁止(例如<RLO>elgoog<PDF>.com会显示google.com).但他们会在这里工作.
使用LRO/PDF将整个字符串括起来强制它从左到右.然后,您将替换用RLO/PDF括起来强制它从右到左.同样,这是最后的手段,但它可以让您完全控制布局:
NSString * const LRO = @"\u202d";
NSString * const RLO = @"\u202e";
NSString * const PDF = @"\u202c";
NSString *test = [@[LRO, @"this is a %25test%25 string", PDF] componentsJoinedByString:@""];
NSString *arabic = @"??????";
NSString *replaceString = [@[RLO, arabic, PDF] componentsJoinedByString:@""];
NSString *result = [test stringByReplacingOccurrencesOfString:@"test" withString:replaceString];
Run Code Online (Sandbox Code Playgroud)
我认为你可以使用显式定向嵌入字符来解决这个问题,但是我没有真正找到一种方法来做到这一点而没有至少一个覆盖(例如,你可以使用RLE而不是上面的RLO,但你仍然需要LRO).
这些应该为您提供所需的工具来解决所有问题.有关详细信息,请参阅Unicode TR9.如果您想要更深入地介绍问题和解决方案,请参阅Cal Henderson 在Unicode中出色的理解双向(BIDI)文本.