How to Find & Replace text in a file using Objective C? - objective-c

I am new to Xcode and was wondering if anyone could help me with this.
I need to make an application that is able to open a file and replace its contents.
E.g. (in psuedo code)
Replace("String1", "String2", "~/Desktop/Sample.txt")
Please let me know if I'm not clear enough.
Thanks in advance.

use stringByReplacingOccurrencesOfString:withString: method which will find all occurrences of one NSString and replace them, returning a new autoreleased NSString.
NSString *source = #"The rain in Spain";
NSString *copy = [source stringByReplacingOccurrencesOfString:#"ain"
withString:#"oof"];
NSLog(#"copy = %#", copy);
// prints "copy = The roof in Spoof"
Edit
to set the file content in your string (be careful , this is not conveniant if your file is a bit large) , replace occurences then copy to a new file :
// Instantiate an NSString which describes the filesystem location of
// the file we will be reading.
NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:#"Sample.txt"];
NSError *anError;
NSString *aString = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:&anError];
// If the file read was unsuccessful, display the error description.
// Otherwise, copy the string to your file.
if (!aString) {
NSLog(#"%#", [anError localizedDescription]);
} else {
//replace string1 occurences by string2
NSString *replacedString = [aString stringByReplacingOccurrencesOfString:#"String1"
withString:#"String2"];
//copy replacedString to sample.txt
NSString * stringFilepath = #"ReplacedSample.txt";
[replacedString writeToFile:stringFilepath atomically:YES encoding:NSWindowsCP1250StringEncoding error:error];
}

You probably want this
And regarding your question about how to read the text from a file to a NSString:
NSError * error;
NSString * stringFromFile;
NSString * stringFilepath = #"loadfile.txt";
stringFromFile = [[NSString alloc] initWithContentsOfFile:stringFilepath
encoding:NSWindowsCP1250StringEncoding
error:&error];
And for writing to a file:
(using the same NSString from loading: stringFromFile)
NSError * error;
NSString * stringFilepath = #"savefile.txt";
[stringFromFile writeToFile:stringFilepath atomically:YES encoding:NSWindowsCP1250StringEncoding error:error];
Note that in this example i use an encoding for windows (this means it uses charcters \n\r at the end of each line). Check the documentation for other types of encoding.
(See NSString documentation)

For Xcode 4, open the file you want to search, then click Edit > Find > Find and Replace, or the keyboard shortcut Command + Option + f.

Related

Text encoding error when opening Notification Center plist using stringWithContentsOfFile:encoding:error:

I want to write a function that finds out whether "Do Not Disturb" is turned on, on a Mac.
So I have written this function:
-(BOOL)dndIsOn{
NSString* path = [[NSString stringWithFormat:#"~/Library/Preferences/ByHost/com.apple.notificationcenterui.%#.plist",[self getSystemUUID]] stringByExpandingTildeInPath];
NSError *error = nil;
NSString *txtFileContents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
NSLog(#"string:%# error:%#",txtFileContents,error);
if ([txtFileContents rangeOfString:#"<fals"].location == NSNotFound) {
return false;
}else{
return true;
}
}
But the txtFileContents string error returns:
.... .plist” couldn’t be opened using text encoding Unicode (UTF-8)."
Why is this happening?
You can read a .plist which top level is a dictionary using dictionaryWithContentsOfFile:. In case the top level is a array, you have to use arrayWithContentsOfURL:. This will automatically parse the file.
In your case, the top level is a dictionary.
The key is doNotDisturb.
The value you want to return:
BOOL doNotDisturbSet = [[NSDictionary dictionaryWithContentsOfFile:path][#"doNotDisturb"]] boolValue];

Read .mobileprovisioning profile with Objective-C

So, I'm trying to open a .mobileprovisioning profile to read what's inside... this is what I'm doing:
NSString *path = [pathURL path];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
Of course I get the data read but I'm not finding the way of getting of get this data into something useful... an NSDictionary, an NSString or whatever...
I've already tried:
NSString *newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
Any idea? I'm sure this is an encoding issue, but I can't solve it after reading and googling for some time... I think the provisioning profile is saved as hexadecimal, but I don't know how to read that from objective-c. I have found this but there wasn't an useful answer.
How to convert NData populated with hex values to NSString
Thanks!
The following method should do what you want. As #rbrockerhoff says the mobile provisioning profile is an encoded CMS message. This method uses a decoder to first decode the data using the CMS functions and then creates the plist string/contents from the decoded data. This string can then be converted into a dictionary which is returned from the method. The dictionary will contain all the details from the mobile provisioning profile.
- (NSDictionary *)provisioningProfileAtPath:(NSString *)path {
CMSDecoderRef decoder = NULL;
CFDataRef dataRef = NULL;
NSString *plistString = nil;
NSDictionary *plist = nil;
#try {
CMSDecoderCreate(&decoder);
NSData *fileData = [NSData dataWithContentsOfFile:path];
CMSDecoderUpdateMessage(decoder, fileData.bytes, fileData.length);
CMSDecoderFinalizeMessage(decoder);
CMSDecoderCopyContent(decoder, &dataRef);
plistString = [[NSString alloc] initWithData:(__bridge NSData *)dataRef encoding:NSUTF8StringEncoding];
NSData *plistData = [plistString dataUsingEncoding:NSUTF8StringEncoding];
plist = [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListImmutable format:nil error:nil]
}
#catch (NSException *exception) {
NSLog(#"Could not decode file.\n");
}
#finally {
if (decoder) CFRelease(decoder);
if (dataRef) CFRelease(dataRef);
}
return plist;
}
A .mobileprovisioning file is an encoded CMS message.
See https://developer.apple.com/library/mac/documentation/security/Reference/CryptoMessageRef/Reference/reference.html for details and an API for decoding it.
If you just want the encoded property list as text, a quick-and-dirty hack is to get the byte pointer for your NSData, scan for the beginning "<?xml" and up to the closing "</plist>". Then make a NSString from that.
You can simply force to open the mobile provisioning profile in TextEdit where you can see the
interior contents and in which you can trim/Edit the encoded CMS message or whatever you want . Then you can simply decode with NSData encodewithUTF string method.
Hope this helps.

Parsing of m3u files in Objective-C for iPhone from file system or URL

The example below should take a link from m3u playlist and add it to anArray. (So I will get the NSArray(NSMutableArray) with certain links in it)
NSString *fileContents = [NSString stringWithContentsOfFile:#"myfile.m3u" encoding:NSUTF8StringEncoding error:NULL];
NSArray *lines = [fileContents componentsSeparatedByString:#"\n"];
NSLog (#"%#",lines);
All the time I had (null) in NSLog Message.
All the time when I try NSLog or if/else statement to check is there is link in array it gives me the null object in it.
After that I thought the problem was in m3u type and I've tried to change type in txt and read. (For those who don't know, M3U is just the text in UTF-8 encoding and the changing type should give the result)
Then I've tried the .txt files but it doesn't work too. So there is the code of it.
//Check if there is my file
NSString *addPath = [[NSBundle mainBundle] pathForResource:#"somefile" ofType:#"m3u" ];
if ([fileMgr fileExistsAtPath:addPath] ) {
NSLog(#"Yes.We see the file");
}
else {
NSLog(#"Nope there is no file");
}
//Rename
NSString *path1 = addPath;
NSString *theNewFilename = [path1 stringByReplacingOccurrencesOfString:#"m3u" withString:#"txt"];
NSLog(#"Renamed file adress is %#", theNewFilename);
//Check if there is our renamed file(file manager was allocated before)
NSString *addPath1 = [[NSBundle mainBundle] pathForResource:#"somefile" ofType:#"txt" ];
if ([fileMgr fileExistsAtPath:addPath1] ) {
NSLog(#"Yes we had the renamed file");
}
else {
NSLog(#"No we don't");
}
Checking is there is m3u file worked fine. I had Addres to Renamed file too. But when it was checking is there is renamed file, there was no file (null in NSLog).
After all that stuff, and without any hope to reach my destination I've tried to read txt file line by line separated by /n with 5 links in it.
NSString *fileContents1 = [NSString stringWithContentsOfFile:#"myfile.txt" encoding:NSUTF8StringEncoding error:NULL];
NSArray *lines1 = [fileContents1 componentsSeparatedByString:#"\n"];
NSLog (#"%#",fileContents1);
NSLog (#"%#",lines1);
Both Messages were NULL
One more thing all this stuff I tried to make in -(IBAction)fileRead { } linked to button
(Yes I've presed button every time to check my NSLog)Program was checked in iPhone Simulator. Will be glad if someone say what is the trouble. Also if there is easier way to make this with url. (Tried Couple times with NSUrl and had Nothing but null )
Just because you've changed the path doesn't mean that you've renamed/moved/copied an item, path is just a string. Use NSFileManager methods like
– moveItemAtURL:toURL:error: or
– moveItemAtPath:toPath:error:.
Also, NSString doesn't care about extension, so it's completely safe to read your m3u file to NSString, no need to rename it.
NSString *addPath = [[NSBundle mainBundle] pathForResource:#"somefile" ofType:#"m3u" ];
if ([fileMgr fileExistsAtPath:addPath] ) {
NSLog(#"Yes.We see the file");
NSString *fileContents1 = [NSString stringWithContentsOfFile:addPath encoding:NSUTF8StringEncoding error:NULL];
NSArray *lines1 = [fileContents1 componentsSeparatedByString:#"\n"];
NSLog (#"%#",fileContents1);
NSLog (#"%#",lines1);
}
else {
NSLog(#"Nope there is no file");
}

Reading float from a custom file

I want to read some float value one by one from a custom file I defined "player.geo".
player.geo is a file I created using Xcode 4 ("Empty File" from the File > New menu)
I'm currently trying to do it like this:
- (id) initWithGeometryFile:(NSString *) nameOfFile
{
NSFileHandle *geoFile = NULL;
NSString *geoFilePath = [[NSBundle mainBundle] pathForResource:#"player" ofType:#"geo"];
geoFile = [NSFileHandle fileHandleForReadingAtPath:geoFilePath];
if(geoFile == NULL)
{
NSLog(#"Failed to open file.");
}
else
{
NSLog(#"Opening %# successful", nameOfFile);
NSMutableData *fileData = [[NSMutableData alloc] initWithData:[geoFile readDataOfLength:4]];
float firstValue;
[fileData getBytes:&firstValue length:sizeof(float)];
NSLog(#"First value in file %# is %f", nameOfFile, firstValue);
}
return self;
}
I'm not getting the expected value of -64.0, rather I'm getting 0.0.
Is this the right way to go about it?
Do I really have to read the file as a string and then parse float the string contents to get the float value?
NSData objects deal with raw bytes, not strings. If you are typing in a string into a txt file, this will not work. If you are using NSData objects, then you will need to first write the data using the data object methods such as writeToFile:atomically:.
Alternately, you can use the NSString functions stringWithContentsOfFile and componentsSeperatedByString to generate an NSArray containing each string on it's own line, like so:
NSString *tmp;
NSArray *lines;
lines = [[NSString stringWithContentsOfFile:#"testFileReadLines.txt"]
componentsSeparatedByString:#"\n"];
NSEnumerator *nse = [lines objectEnumerator];
while(tmp = [nse nextObject]) {
NSLog(#"%#", tmp);
}

Null output troubles

Okay I've been trying at this for about 2-3 hours now and I don't seem to quite get it. Here is the code and a brief explanation:
I'm trying to make two lists of words, pull one word from each of those lists at random, and display both words (along with a third) on the screen when a button is pressed. Here is the code:
#import "Project001ViewController.h"
#implementation Project001ViewController
-(ArrayOfWords *) advs
{
if(!advs){
advs = [[ArrayOfWords alloc] init];
NSString* advpath = #"/WordLists/adverbs.txt";
NSLog(#"1");
[[self advs] populateListOfWords:advpath];
}
return advs;
}
-(ArrayOfWords *) adjs
{
if (!adjs) {
adjs = [[ArrayOfWords alloc] init];
NSString* adjpath = #"/WordLists/adjectives.txt";
[[self adjs] populateListOfWords:adjpath];
NSLog(#"2");
}
return adjs;
}
- (IBAction)generate:(UIButton *)sender;
{
//int randy = arc4random() % 11;
//NSNumber* num= [NSNumber numberWithInteger:randy];
NSString* obj = #"app";
NSString* adverb = [[self advs] randomItem];
NSString* adjective = [[self adjs] randomItem];
NSLog(#"%i %i",[adjs size],[advs size]);
NSLog(#"1 %# %# %#.",adverb, adjective, obj);
//NSLog(#"%#",thePhrase);
[display setText:#"Hi"];
}
#end
I'm having trouble on the last NSLog line:
NSString* obj = #"app";
NSString* adverb = [[self advs] randomItem];
NSString* adjective = [[self adjs] randomItem];
NSLog(#"%i %i",[adjs size],[advs size]);
NSLog(#"1 %# %# %#.",adverb, adjective, obj);
Instead of getting the two randomly selected words (using arc4random() to produce them) the array returns Null. But I know FOR CERTAIN. That the array's are not empty because the NSLog Line where I print [adjs size] and [advs size] I get the correct sizes of the list of words. I just want to know what is causing them to print Null here.
populateListOfWords, randomItem, and size methods:
- (NSArray *) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
length=(NSNumber*)([words count]);
return words;
}
-(NSString*) randomItem{
//returns random object in list
return (NSString*)[list objectAtIndex:(arc4random() % (int)length)] ;
}
-(int) size{
//returns size of list
return (int)length;
}
(If more code is needed let me know and thank you in advanced for any and all help).
I believe there is a problem with the paths. It is impossible to have access to the path /WordLists/adjectives.txt in iOS due to the application sandbox. I suggest you add these files to the application by dragging and dropping them onto the project. You can get the file paths for resources in application bundle using
NSString * path = [[NSBundle mainBundle] pathForResource:#"adjectives" ofType:#"txt"];
Now pass this path to the method populateListOfWords:.
Because of the incorrect path, I believe wordListString is nil and everything else follows to be that.
Another thing is that int and NSNumber are not toll free bridged like NSStrings and other foundation objects. So
length=(NSNumber*)([words count]);
is incorrect. I suggest you define length as int or better NSUInteger to match the type count method returns.
This method is the problem:
- (NSArray *) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
length=(NSNumber*)([words count]);
return words;
}
It wasn't actually putting the words in a list that anyone else could access. I had to just modify it like so:
- (void) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
list = words;
length=(int)([words count]);
}
Now it gives me the correct output. But for some reason when I press the button twice it crashes. Oh well that's a new problem. Thanks again for all the help.
UPDATE
Turns out advs and adjs were being released so the second go around it was trying to access a nil value because when I call [self advs] [self adjs] the pointers exist, but their contents do not. I had to go back and refill them each time basically removing the if (!advs) and if (adjs) parts. It now works as intended.