Reading a File Character by character in iOS - objective-c

Basically i have a concern, which needs to be solved using Objective C alone. (i have tried with C)
Is there any appropriate way in objective C to read character by character (till EOF) from a file, which is placed in document directory. So, I will append a escape character before all inverted comma's in a file and A special character (say /) before each line.
Replace
(type == "Project")
with
/(doc.type == \"Project\")
if u feel my approach is not correct is there any other method to accomplish this task in an efficient way?

Get all the lines from your file, make your changes such as replacing characters or adding more text, and then save the file.
NSString *objPath = [[NSBundle mainBundle] pathForResource:#"textfile" ofType:#"txt"];
NSData *objData = [[NSData alloc] initWithContentsOfFile:objPath];
NSString *objString = [[NSString alloc] initWithData:objData encoding:NSUTF8StringEncoding];
NSMutableArray *lines = [NSMutableArray arrayWithArray:[objString componentsSeparatedByString:#"\n"]];
for (int i = 0; i < lines.count; i++) {
NSString *oneLine = [lines objectAtIndex:i];
if (oneLine.length < 2) {
continue;
}
oneLine = [NSString stringWithFormat:#"/%#", oneLine];
oneLine = [oneLine stringByReplacingOccurrencesOfString:#"`" withString:#"\`"];
[lines replaceObjectAtIndex:i withObject:oneLine];
}
NSString *finalString = [lines componentsJoinedByString:#"\n"];
//save the file

You can use NSFileHandle and invoke readDataAtLength: with a value of 1.

Related

Get a substring from an NSString until arriving to any letter in an NSArray - objective C

I am trying to parse a set of words that contain -- first greek letters, then english letters. This would be easy if there was a delimiter between the sets.That is what I've built so far..
- (void)loadWordFileToArray:(NSBundle *)bundle {
NSLog(#"loadWordFileToArray");
if (bundle != nil) {
NSString *path = [bundle pathForResource:#"alfa" ofType:#"txt"];
//pull the content from the file into memory
NSData* data = [NSData dataWithContentsOfFile:path];
//convert the bytes from the file into a string
NSString* string = [[NSString alloc] initWithBytes:[data bytes]
length:[data length]
encoding:NSUTF8StringEncoding];
//split the string around newline characters to create an array
NSString* delimiter = #"\n";
incomingWords = [string componentsSeparatedByString:delimiter];
NSLog(#"incomingWords count: %lu", (unsigned long)incomingWords.count);
}
}
-(void)parseWordArray{
NSLog(#"parseWordArray");
NSString *seperator = #" = ";
int i = 0;
for (i=0; i < incomingWords.count; i++) {
NSString *incomingString = [incomingWords objectAtIndex:i];
NSScanner *scanner = [NSScanner localizedScannerWithString: incomingString];
NSString *firstString;
NSString *secondString;
NSInteger scanPosition;
[scanner scanUpToString:seperator intoString:&firstString];
scanPosition = [scanner scanLocation];
secondString = [[scanner string] substringFromIndex:scanPosition+[seperator length]];
// NSLog(#"greek: %#", firstString);
// NSLog(#"english: %#", secondString);
[outgoingWords insertObject:[NSMutableArray arrayWithObjects:#"greek", firstString, #"english",secondString,#"category", #"", nil] atIndex:0];
[englishWords insertObject:[NSMutableArray arrayWithObjects:secondString,nil] atIndex:0];
}
}
But I cannot count on there being delimiters.
I have looked at this question. I want something similar. This would be: grab the characters in the string until an english letter is found. Then take the first group to one new string, and all the characters after to a second new string.
I only have to run this a few times, so optimization is not my highest priority.. Any help would be appreciated..
EDIT:
I've changed my code as shown below to make use of NSLinguisticTagger. This works, but is this the best way? Note that the interpretation for english characters is -- for some reason "und"...
The incoming string is: άγαλμα, το statue, only the last 6 characters are in english.
int j = 0;
for (j=0; j<incomingString.length; j++) {
NSString *language = [tagger tagAtIndex:j scheme:NSLinguisticTagSchemeLanguage tokenRange:NULL sentenceRange:NULL];
if ([language isEqual: #"und"]) {
NSLog(#"j is: %i", j);
int k = 0;
for (k=0; k<j; k++) {
NSRange range = NSMakeRange (0, k);
NSString *tempString = [incomingString substringWithRange:range ];
NSLog (#"tempString: %#", tempString);
}
return;
}
NSLog (#"Language: %#", language);
}
Alright so what you could do is use NSLinguisticTagger to find out the language of the word (or letter) and if the language has changed then you know where to split the string. You can use NSLinguisticTagger like this:
NSArray *tagschemes = #[NSLinguisticTagSchemeLanguage];
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:tagschemes options: NSLinguisticTagPunctuation | NSLinguisticTaggerOmitWhitespace];
[tagger setString:#"This is my string in English."];
NSString *language = [tagger tagAtIndex:0 scheme:NSLinguisticTagSchemeLanguage tokenRange:NULL sentenceRange:NULL];
//Loop through each index of the string's characters and check the language as above.
//If it has changed then you can assume the language has changed.
Alternatively you can use NSSpellChecker's requestCheckingOfString to get teh dominant language in a range of characters:
NSSpellChecker *spellChecker = [NSSpellChecker sharedSpellChecker];
[spellChecker setAutomaticallyIdentifiesLanguages:YES];
NSString *spellCheckText = #"Guten Herr Mustermann. Dies ist ein deutscher Text. Bitte löschen Sie diesen nicht.";
[spellChecker requestCheckingOfString:spellCheckText
range:(NSRange){0, [spellCheckText length]}
types:NSTextCheckingTypeOrthography
options:nil
inSpellDocumentWithTag:0
completionHandler:^(NSInteger sequenceNumber, NSArray *results, NSOrthography *orthography, NSInteger wordCount) {
NSLog(#"dominant language = %#", orthography.dominantLanguage);
}];
This answer has information on how to detect the language of an NSString.
Allow me to introduce two good friends of mine.
NSCharacterSet and NSRegularExpression.
Along with them, normalization. (In Unicode terms)
First, you should normalize strings before analyzing them against a character set.
You will need to look at the choices, but normalizing to all composed forms is the way I would go.
This means an accented character is one instead of two or more.
It simplifies the number of things to compare.
Next, you can easily build your own NSCharacterSet objects from strings (loaded from files even) to use to test set membership.
Lastly, regular expressions can achieve the same thing with Unicode Property Names as classes or categories of characters. Regular expressions could be more terse but more expressive.

ObjectiveC - Read from console string with multiple lines (Breaks\newLine)

Is there a way in objectiveC to read a string with multiple lines from user input in the console?
I'm making a simple console app in Xcode, which i want people to be able to paste SQL queries which may be in few lines.
Currently i'm using:
char strIn[512];
NSMutableString* str;
scanf("%[^\n]", strIn);
str = [NSString stringWithUTF8String:strIn];
NSLog(#"\n%#",str);
But this only gets the first line.
Remember - this is not about reading from file, but only from the console.
thanks.
The following code should help you to get started. It reads from standard input and collects the input lines in the inputString variable, until a semi-colon is found.
NSFileHandle *inputFile = [NSFileHandle fileHandleWithStandardInput];
NSMutableString *inputString = [NSMutableString string];
do {
// Read from stdin, check for EOF:
NSData *data = [inputFile availableData];
if ([data length] == 0) {
NSLog(#"EOF");
break;
}
// Convert to NSString, replace newlines by spaces, append to current input:
NSMutableString *tmp = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[tmp replaceOccurrencesOfString:#"\n" withString:#" " options:0 range:NSMakeRange(0, [tmp length])];
[inputString appendString:tmp];
// Check for semi-colon:
} while ([inputString rangeOfString:#";"].location == NSNotFound);
NSLog(#"input=%#", inputString);
(Note that this sample code simple checks for a semi-colon somewhere in the input. It does not check if the semi-colon is e.g. embedded in a string.)

Filtering out BOM characters from an NSXMLDocument

The stringValue of some elements from an XML files contain BOM characters in them. The xml file is marked as UTF-8 encoding.
Some of those characters are at the beginning of the string (as it should be from what I read about it) but some are in the middle of the string (malformed string from whoever wrote the xml file maybe?).
I'm opening the file with:
NSURL *furl = [NSURL fileURLWithPath:fileName];
if (!furl) {
NSLog(#"Error: Can't open NML file '%#'.", fileName);
return kNxADbReaderTTError;
}
NSError *err=nil;
NSXMLDocument *xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl options:NSXMLNodeOptionsNone error:&err];
And I query the element this way:
NSXMLElement *anElement;
NSString *name;
...
NSString *valueString = [[anElement attributeForName:name] stringValue];
My questions are:
Am I opening the file wrong? Is the file malformed? Am I querying the string value of the element wrong? How can I filter those characters out?
While fixing another issue, I found a relatively clean way of filtering out unwanted characters from the source of an NSXMLDocument. Pasting it here just in case someone encounters a similar issue:
#implementation NSXMLDocument (FilterIllegalCharacters)
- (NSXMLDocument *)initWithDataAndIgnoreIllegalCharacters:(NSData *)data illegalChars:(NSCharacterSet *)illegalChars error:(NSError **)error{
// -- Then, read the resulting XML string.
NSMutableString *str = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// -- Go through the XML, only caring about attribute value strings
NSMutableArray *charactersToRemove = [NSMutableArray array];
NSUInteger openQuotes = NSNotFound;
for (NSUInteger pos = 0; pos < str.length; ++pos) {
NSUInteger currentChar = [str characterAtIndex:pos];
if (currentChar == '\"') {
if (openQuotes == NSNotFound) {
openQuotes = pos;
}
else {
openQuotes = NSNotFound;
}
}
else if (openQuotes != NSNotFound) {
// -- If we find an illegal character, we make a note of its position.
if ([illegalChars characterIsMember:currentChar]) {
[charactersToRemove addObject:[NSNumber numberWithLong:pos]];
}
}
}
if (charactersToRemove.count) {
NSUInteger index = charactersToRemove.count;
// -- If we have characters to fix, we work thru them backwards, in order to not mess up our saved positions by modifying the XML.
do {
--index;
NSNumber *characterPos = charactersToRemove[index];
[str replaceCharactersInRange:NSMakeRange(characterPos.longValue, 1) withString:#""];
}
while (index > 0);
// -- Finally we update the data with our corrected version
data = [str dataUsingEncoding:NSUTF8StringEncoding];
}
return [[NSXMLDocument alloc] initWithData:data options:NSXMLNodeOptionsNone
error:error];
}
#end
You can pass any character set you want. Note that this sets the options for reading the XML document to none. You might want to change this for your own purposes.
This only filters the content of attributes strings, which is where my malformed string came from.

Objective C Adding content to a file

I need to write several line to a file. How can I move to the next line so that the file content is not overwritten each time? I am using a for loop with the following code in it
[anNSString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
The NSString. anNSString is reinitialized during each loop. SO i need to keep adding to the file path each during each loop.
Thanks
I feel like the accepted answer is not correct since it didn't answer the original question. To solve the original question you should use an NSOutputStream it makes appending content to an existing file an easy task:
NSString *myString = #"Text to append!"; // don't forget to add linebreaks if needed (\r\n)
NSOutputStream *stream = [[NSOutputStream alloc] initToFileAtPath:filePath append:YES];
[stream open];
NSData *strData = [myString dataUsingEncoding:NSUTF8StringEncoding];
[stream write:(uint8_t *)[strData bytes] maxLength:[strData length]];
[stream close];
You just write it out all at once, rather than attempting to write it incrementally. -[NSString writeToFile:atomically:encoding:error] will just overwrite the file each time - it does not append.
Here's an illustration:
NSMutableString * str = [NSMutableString new];
// > anNSString is reinitialized during each loop.
for ( expr ) {
NSString * anNSString = ...;
// > SO i need to keep adding to the file path each during each loop.
[str appendString:anNSString];
}
NSError * outError(0);
BOOL success = [str writeToFile:path
atomically:YES
encoding:NSUTF8StringEncoding
error:&outError];
[str release];
...
If you need to write to a new line each time, start with what is in #justin's answer, but add [str appendString:#"\r\n"]; wherever you need new lines.
NSMutableString * str = [NSMutableString new];
// > anNSString is reinitialized during each loop.
for ( expr ) {
NSString * anNSString = ...;
// > SO i need to keep adding to the file path each during each loop.
[str appendString:anNSString];
[str appendString:#"\r\n"]; //****** THIS IS THE NEW LINE ******
}
NSError * outError(0);
BOOL success = [str writeToFile:path
atomically:YES
encoding:NSUTF8StringEncoding
error:&outError];
[str release];
...
With this code, each anNSString will be on it's own line in the text file.

Reading ints from NSData?

I think I am getting a little confused here, what I have is a plain text file with the numbers "5 10 2350" in it. As you can see below I am trying to read the first value using readDataOfLength, I think maybe where I am getting muddled is that I should be reading as chars, but then 10 is 2 chars and 2350 is 4. Can anyone point m in the right direction to reading these.
NSString *dataFile_IN = #"/Users/FGX/Documents/Xcode/syntax_FileIO/inData.txt";
NSFileHandle *inFile;
NSData *readBuffer;
int intBuffer;
int bufferSize = sizeof(int);
inFile = [NSFileHandle fileHandleForReadingAtPath:dataFile_IN];
if(inFile != nil) {
readBuffer = [inFile readDataOfLength:bufferSize];
[readBuffer getBytes: &intBuffer length: bufferSize];
NSLog(#"BUFFER: %d", intBuffer);
[inFile closeFile];
}
EDIT_001
Both excellent answers from Jarret and Ole, here is what I have gone with. One final question "METHOD 02" picks up a carriage return to a blank line at the bottom of the text file, returns it as a subString, which in turn gets converted to "0" can I set the NSCharacterSet to stop that, currently I just added a length check on the string.
NSInteger intFromFile;
NSScanner *scanner;
NSArray *subStrings;
NSString *eachString;
// METHOD 01 Output: 57 58 59
strBuffer = [NSString stringWithContentsOfFile:dataFile_IN encoding:NSUTF8StringEncoding error:&fileError];
scanner = [NSScanner scannerWithString:strBuffer];
while ([scanner scanInteger:&intFromFile]) NSLog(#"%d", intFromFile);
// METHOD 02 Output: 57 58 59 0
strBuffer = [NSString stringWithContentsOfFile:dataFile_IN encoding:NSUTF8StringEncoding error:&fileError];
subStrings = [strBuffer componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
for(eachString in subStrings) {
if ([eachString length] != 0) {
NSLog(#"{%#} %d", eachString, [eachString intValue]);
}
}
gary
There are several conveniences in Cocoa that can make your life a bit easier here:
NSString *dataFile_IN = #"/Users/FGX/Documents/Xcode/syntax_FileIO/inData.txt";
// Read all the data at once into a string... an convenience around the
// need the open a file handle and convert NSData
NSString *s = [NSString stringWithContentsOfFile:dataFile_IN
encoding:NSUTF8StringEncoding
error:nil];
// Use a scanner to loop over the file. This assumes there is nothing in
// the file but integers separated by whitespace and newlines
NSInteger anInteger;
NSScanner *scanner = [NSScanner scannerWithString:s];
while (![scanner isAtEnd]) {
if ([scanner scanInteger:&anInteger]) {
NSLog(#"Found an integer: %d", anInteger);
}
}
Otherwise, using your original approach, you'd pretty much have to read character-by-character, adding each character to a "buffer" and then evaluating your integer when you encounter a space (or newline, or some other separator).
If you read the file's contents into a string as Jaret suggested, and assuming the string only contains numbers and whitespace, you can also call:
NSArray *substrings = [s componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
This will split the string at whitespace and newline characters and return an array of the substrings. You would then have to convert the substrings to integers by looping over the array and calling [substring integerValue].
One way to do it would be first to first turn your readBuffer into a string as follows:
NSString * dataString = [[NSString alloc] initWithData:readBuffer encoding:NSUTF8StringEncoding];
Then split the string into values:
NSString *dataString=#"5 10 2350"; // example string to split
NSArray * valueStrings = [dataString componentsSeparatedByString:#" "];
for(NSString *valueString in valueStrings)
{
int value=[valueString intValue];
NSLog(#"%d",value);
}
Output of this is
5
10
2350