I have a data file that was converted from an RTF to a TXT. When I started trying to parse it using PERL, my regular expressions weren't able to split up lines that looked like they had whitespace delimiters - It would just ignore the whitespace.
After my initial confusion, I figured that the whitespace must be something other than an ASCII space character, tab, etc. By some experimentation, I noticed that there were several bytes being represented in that "whitespace".
To try and figure out what the bytes/characters were, I created a little PERL code segment that looked like:
while ($filecontents =~ /([^\d\w\s\t\.:;&\,\-\(\)]+)/){
$f = $1;
$d = $1;
$f =~ s/(.)/sprintf("%x ",ord($1))/eg;
print "f is $f\n";
$filecontents =~ s/$d/zzz/g;
}
Basically, the code goes thru the file, finds oddball characters and prints them out. When I ran it, it produced the following:
f is e2 80 83
f is e2 80 a8
f is e2 81 84
f is c2 b0
Note that each of those looks like a multi-byte character, but what are they?
Well, I do love the internet. I cut and pasted e2 80 a8 into Google and found that it was an "em space", aka Unicode character \u2003.
Once I was able to get the Unicode character, I could just replace all of the em spaces with a regular space, and the rest of my program worked as designed. Same idea with the other special characters. Two of those characters were not whitespace, but were non-ASCII characters as well (fraction slash and degree symbol).
Note that, at least in my case, I had to match using the hex versus the unicode character. In other words
$filecontents =~ s/\xe2\x80\xa8/ /g;
I'm assuming this is because the Unicode would be a UTF-16 character but I'm dealing with a UTF-8 encoding? For next time, I should see if I can export the RTF to a UTF-16 text file. Maybe it would be easier :)
Sunday, February 16, 2014
Monday, February 10, 2014
UIWebView, URL history, and redirects
Sometimes it seems the simplest things turn out to be much more complicated than they should be.
In several of our apps, we include help files written in HTML which are loaded locally from the bundle into a UIWebView. Sometimes those help files contain links to web pages.
The problem is that UIWebView doesn't treat locally loaded webpages as part of the history stack. Thus if the user clicks a link and visits a web page, there is no simple way for the app to return to the original locally loaded HTML file because [webview canGoBack] returns NO. Grr.
My first attempt to deal with the issue was to just reload the local file if [webview canGoBack] was NO. However, if the same locally loaded HTML file contains two web pages, and the user visits each, after the attempt to return from the 2nd webpage, [webview canGoBack] will return YES and [webview goBack] will display the first webpage, because the first webpage was never removed from the UIWebView's history, and there's apparently no way (that I was able to find) to get rid of it. Grrr.
The next step was to try and maintain my own count of visited URL's and back out of them as the user clicked the back button. I implemented this by adding to the count in the UIWebView delegate method shouldStartWithRequest when the navigationType was UIWebViewNavigationTypeLinkClicked and removed them with each click of the back button. Great! Except that redirects also load with a navigationType of UIWebViewNavigationTypeLinkClicked so the user would have to click "back" several times for no clear reason because the link count was incremented for each redirect. Grrrr.
I was able to finally make it work with some insight from this tutorial. The key I learned was that the webViewDidFinishLoad delegate method is not called until the redirects have been resolved. (But it may call the method several times as it loads the contents of the page).
Below are the key elements of the code:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// we use the linkClicked boolean because with redirects, this method is still called multiple times and we only want
// to increment once.
if (linkClicked == NO && navigationType == UIWebViewNavigationTypeLinkClicked){
linkClicked = YES;
if (linkStack == 0){
scrollOffset = webView.scrollView.contentOffset;
}
linkStack++;
}
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if (linkStack == 0 && scrollOffset.y > 0){
// reset the scroll if we are coming back to a page after a clicked link.
[wv.scrollView setContentOffset:scrollOffset];
scrollOffset = CGPointMake(0, 0);
}
NSURLRequest* request = [webView request];
if ([[request mainDocumentURL] isEqual:lastMainDoc])
return;
linkClicked = NO;
[self setLastMainDoc:[request mainDocumentURL]];
NSLog(@"finished loading %@", [[request mainDocumentURL] absoluteString]);
}
-(void)doBack:(id)sender
{
if (linkStack > 0){
if (linkStack == 1 || [wv canGoBack] == NO){
[self loadFile];
} else {
linkStack--;
[wv goBack];
}
return;
}
}
- (void)loadFile
{
// load local HTML file
//....
}
I'm sure there are a few other ways to address this problem, but this is what worked for me....
In several of our apps, we include help files written in HTML which are loaded locally from the bundle into a UIWebView. Sometimes those help files contain links to web pages.
The problem is that UIWebView doesn't treat locally loaded webpages as part of the history stack. Thus if the user clicks a link and visits a web page, there is no simple way for the app to return to the original locally loaded HTML file because [webview canGoBack] returns NO. Grr.
My first attempt to deal with the issue was to just reload the local file if [webview canGoBack] was NO. However, if the same locally loaded HTML file contains two web pages, and the user visits each, after the attempt to return from the 2nd webpage, [webview canGoBack] will return YES and [webview goBack] will display the first webpage, because the first webpage was never removed from the UIWebView's history, and there's apparently no way (that I was able to find) to get rid of it. Grrr.
The next step was to try and maintain my own count of visited URL's and back out of them as the user clicked the back button. I implemented this by adding to the count in the UIWebView delegate method shouldStartWithRequest when the navigationType was UIWebViewNavigationTypeLinkClicked and removed them with each click of the back button. Great! Except that redirects also load with a navigationType of UIWebViewNavigationTypeLinkClicked so the user would have to click "back" several times for no clear reason because the link count was incremented for each redirect. Grrrr.
I was able to finally make it work with some insight from this tutorial. The key I learned was that the webViewDidFinishLoad delegate method is not called until the redirects have been resolved. (But it may call the method several times as it loads the contents of the page).
Below are the key elements of the code:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// we use the linkClicked boolean because with redirects, this method is still called multiple times and we only want
// to increment once.
if (linkClicked == NO && navigationType == UIWebViewNavigationTypeLinkClicked){
linkClicked = YES;
if (linkStack == 0){
scrollOffset = webView.scrollView.contentOffset;
}
linkStack++;
}
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if (linkStack == 0 && scrollOffset.y > 0){
// reset the scroll if we are coming back to a page after a clicked link.
[wv.scrollView setContentOffset:scrollOffset];
scrollOffset = CGPointMake(0, 0);
}
NSURLRequest* request = [webView request];
if ([[request mainDocumentURL] isEqual:lastMainDoc])
return;
linkClicked = NO;
[self setLastMainDoc:[request mainDocumentURL]];
NSLog(@"finished loading %@", [[request mainDocumentURL] absoluteString]);
}
-(void)doBack:(id)sender
{
if (linkStack > 0){
if (linkStack == 1 || [wv canGoBack] == NO){
[self loadFile];
} else {
linkStack--;
[wv goBack];
}
return;
}
}
- (void)loadFile
{
// load local HTML file
//....
}
I'm sure there are a few other ways to address this problem, but this is what worked for me....
Friday, February 8, 2013
Using -webkit-tap-highlight-color
In earlier versions of mobile safari if you used -webkit-tap-highlight-color on a link, you'd also have to specify the active color. That would give you a nice contrast. However, since about iOS 5 that no longer works - both the foreground and background are set to the highlight color.
To be able to see the text now, it seems that you need a transparent color. This works:
-webkit-tap-highlight-color:rgba(26,26,26,.5);
To be able to see the text now, it seems that you need a transparent color. This works:
-webkit-tap-highlight-color:rgba(26,26,26,.5);
Wednesday, February 6, 2013
Wrapping text around an image AND stopping the wrap.
I like being able to use HTML to wrap text around images. It looks professional and it's a simple way to inject some cool in an HTML page.
However, sometimes the image is bigger than the text, and at that point you may want to start a new paragraph that is beyond the image. I was never able to find out how until today: Just needs
<BR CLEAR="left">
See? Down here now! Note that "left" can be other values (e.g., "right","all") to configure appropriately.
Friday, February 1, 2013
Using Google TTS (text to speech)
I was doing some investigation on Text-To-Speech (TTS) for iOS and found a little snippet of code that could be added to any app for some quick TTS.
The catch is that it's limited to just 100 characters and you never know when Google might pull the plug on it, but still, it's pretty cool.
The catch is that it's limited to just 100 characters and you never know when Google might pull the plug on it, but still, it's pretty cool.
#import <AVFoundation/AVFoundation.h>
...
NSString *linkTTS = [NSString stringWithFormat:@"http://translate.google.com/translate_tts?tl=en&q=%@",@"this+is+really+quite+cool"];
NSData *dataTTS = [NSData dataWithContentsOfURL:[NSURL URLWithString:linkTTS]];
AVAudioPlayer *_googlePlayer = [[AVAudioPlayer alloc] initWithData:dataTTS error:nil];
[_googlePlayer play];
Friday, June 15, 2012
Add Localized Help Files Maintaining Subfolder Hierarchy in XCode 4
One of my projects has localized help files for the app. When the help was originally designed, it was put into subfolders for major help topics. That didn't really cause any problems until I migrated it into XCode 4. There it took a little bit of experimentation before I figured it out.
Basically I had to
Basically I had to
- Click on the target
- Select File->Add Files to...
- Add the en.lproj/help directory and then make sure to Create folder references for any added folders
- Then, select the newly created blue help item in the project navigator and then click on the File->Add Files to.. for the other languages (e.g., es.lproj/help) etc. If you try to do this by clicking on the target first (instead of the help item in the navigator) it'll cause a compiler error since it (apparently) tries to overwrite the subdirectories
Wednesday, April 25, 2012
WWDC 2012 - A Guide For Those That Can't Go
For those of us that didn't get WWDC tickets, I've decided to write up some tips to make us feel like we're there (based on my experience the last two years). So let's keep our chins up, eh? Here goes:
- Send me $1600.00. Part of WWDC's mystique is writing a large check for others to tell you what to do. This is a necessary step. Make sure you do it.
THE DAY BEFORE
- Register. Have your S.O. misspell your name on a small piece of paper and attach it with a shoestring. Write WWDC 2012 on a piece of clothing (jacket, t-shirt) and wear it EVERYWHERE for the next five days.
THE FIRST DAY
- Form a single file line outside of your house (you may need to draw on kids, pets, etc). This will simulate the early gathering of dedicated engineers outside of Moscone before the keynote speech. While Tim Cook might not be as big of a draw as Steve was (RIP Steve), you may still want to get in line no later than 11PM the night before; you'll want to be able to sit up front.
If you can get a neighbor to walk down your "line" and offer you magazines no one reads, that would be ideal.
- Once inside Moscone (your house) walk into a small closet filled with clothes, lukewarm coffee, a few bagel fragments and a banana. This represents the "pre-keynote" activity where everyone is standing in the grand hallways. It does get a little tight. Stand there until you get tired, and then sit down and pull out your laptop. Complain about the Wifi.
- After 50 minutes or so, get up and walk out of the closet and go upstairs for the keynote. Hurry and find a seat. Stragglers will need to stay downstairs in "the overflow room" since the ballroom will be filled.
- When the keynote starts, you may find yourself clapping for no particular reason. Write something clever on your iPad and hold it over your head. Smile a lot. You're at WWDC!!!
POST-KEYNOTE LUNCH
- Go downstairs. Form a line in front of a table with 5000 boxes of food. After 15 minutes, grab a box of food and attempt to find a seat. Sit on the floor if there are no tables. You can get a drink but, for maximum realism, you should only stock Diet Sprite.
ATTEND A SESSION
- Pick a session and form a line outside your den.
Uh oh. You're turned away at the door; this session is full. Stand in line outside your bedroom instead. Listen to a session talking about hardware optimization on the Newton. It's okay to leave early.
OTHER ACTIVITIES
- Set up a pseudo Apple knick-knacks and branded clothing store. Be sure to overcharge yourself.
ATTEND THE THURSDAY NIGHT EVENT
- Hang out with 5,000 of your closest friends and listen to a band only five of you have ever heard of. Smile a lot. You're at WWDC!!!
NOW
Subscribe to:
Posts (Atom)
