I'm trying to write a series of attributed strings to an RTF file, line-by-line at various points in my application sit is running (you can think of this as log data, only with attributes). The file is created just fine and it would appear that all the data is being written to the file, but when I open the RTF file only the first line written to the file appears. I suspect that there's something written to the file in the first write that essentially end the RTF file, but I'm not quite sure what that is.
- (void) writeLineWithSizeAndStyle: (NSString *) line : (CGFloat)fontSize : (NSFontTraitMask) traits {
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:line];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
NSFontManager *fm = [NSFontManager sharedFontManager];
NSRange range = [line rangeOfString:line];
NSFont *font = [NSFont systemFontOfSize:fontSize];
font = [fm convertFont:font toHaveTrait:traits];
[attrString addAttribute: NSFontAttributeName value:font range:range];
NSData *rtfData = [attrString RTFFromRange:NSMakeRange(0, attrString.length) documentAttributes:#{}];
// Go the the end of the file, write the data, and close the file
[fileHandle seekToEndOfFile];
[fileHandle writeData: rtfData];
[fileHandle closeFile];
}
Is there something I need to to in converting each attributed string to NSData that effectively tells RFT not to terminate the file upon writing?
Thanks!
Update:
Here's the code for solution 2 I proposed in the comments section. This operates on an instance variable in the class object (playByPlay) that simply appends each new line onto the entire "log". This works great, but again is only really a solution for sufficiently small files.
- (void) writeLineWithSizeAndStyle: (NSString *) line : (CGFloat)fontSize : (NSFontTraitMask) traits {
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:line];
NSRange range = [line rangeOfString:line];
NSFontManager *fm = [NSFontManager sharedFontManager];
NSFont *font = [NSFont systemFontOfSize:fontSize];
font = [fm convertFont:font toHaveTrait:traits];
[attrString addAttribute: NSFontAttributeName value:font range:range];
[playByPlayText appendAttributedString:attrString];
NSData *rtfData = [playByPlayText RTFFromRange:NSMakeRange(0, playByPlayText.length) documentAttributes:#{}];
[fileHandle writeData: rtfData];
[fileHandle closeFile];
}
Related
OK, here we are :
I've got an NSTextView
I'm getting it's NSMutableAttributedString content
I'm unable to read/write it to plist
By using Rob's code ( Saving custom attributes in NSAttributedString ), I've made some progress (I'm managing to write the data to disk), but I cannot recover it (= NSKeyedUnarchiver returns nil).
Encoding :
// where MAS --> NSMutableAttributedString
NSData* stringData = [NSKeyedArchiver archivedDataWithRootObject:MAS];
Decoding :
NSMutableAttributedString* mas = (NSMutableAttributedString*)[NSKeyedUnarchiver unarchiveObjectWithData:dat];
Any ideas? Any possible workaround (even if not with NSCoder, which I doubt it works with RTFs...) would be welcome!
And here's how it was solved.
NSAttributedString -> NSData :
NSData* j = [(NSMutableAttributedString*)MAS RTFDFromRange:NSMakeRange(0,[[MAS string] length])
documentAttributes:nil];
NSData -> NSAttributedString
NSMutableAttributedString* mas = [[NSMutableAttributedString alloc] initWithRTFD:dat
documentAttributes:nil];
Simple as that.
I have a log file that I'm trying to append data to the end of. I have an NSMutableString textToWrite variable, and I am doing the following:
[textToWrite writeToFile:filepath atomically:YES
encoding: NSUnicodeStringEncoding error:&err];
However, when I do this all the text inside the file is replaced with the text in textToWrite. How can I instead append to the end of the file? (Or even better, how can I append to the end of the file on a new line?)
I guess you could do a couple of things:
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:aPath];
[fileHandle seekToEndOfFile];
[fileHandle writeData:[textToWrite dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
Note that this will append NSData to your file -- NOT an NSString. Note that if you use NSFileHandle, you must make sure that the file exists before hand. fileHandleForWritingAtPath will return nil if no file exists at the path. See the NSFileHandle class reference.
Or you could do:
NSString *contents = [NSString stringWithContentsOfFile:filepath];
contents = [contents stringByAppendingString:textToWrite];
[contents writeToFile:filepath atomically:YES encoding: NSUnicodeStringEncoding error:&err];
I believe the first approach would be the most efficient, since the second approach involves reading the contents of the file into an NSString before writing the new contents to the file. But, if you do not want your file to contain NSData and prefer to keep it text, the second option will be more suitable for you.
[Update]
Since stringWithContentsOfFile is deprecated you can modify second approach:
NSError* error = nil;
NSString* contents = [NSString stringWithContentsOfFile:filepath
encoding:NSUTF8StringEncoding
error:&error];
if(error) { // If error object was instantiated, handle it.
NSLog(#"ERROR while loading from file: %#", error);
// …
}
[contents writeToFile:filepath atomically:YES
encoding:NSUnicodeStringEncoding
error:&err];
See question on stackoverflow
Initially I thought that using the FileHandler method in the accepted answer that I was going to get a bunch of hex data values written to my file, but I got readable text which is all I need. So based off the accepted answer, this is what I came up with:
-(void) writeToLogFile:(NSString*)content{
content = [NSString stringWithFormat:#"%#\n",content];
//get the documents directory:
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:#"hydraLog.txt"];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:fileName];
if (fileHandle){
[fileHandle seekToEndOfFile];
[fileHandle writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
}
else{
[content writeToFile:fileName
atomically:NO
encoding:NSUTF8StringEncoding
error:nil];
}
}
This way if the file doesn't yet exist, you create it. If it already exists then you only append to it. Also, if you go into the plist and add a key under the information property list UIFileSharingEnabled and set the value to true then the user can sync with their computer and see the log file through iTunes.
And here is a (slightly adopted) Swift version of Chase Roberts' solution:
static func writeToFile(content: String, fileName: String = "log.txt") {
let contentWithNewLine = content+"\n"
let filePath = NSHomeDirectory() + "/Documents/" + fileName
let fileHandle = NSFileHandle(forWritingAtPath: filePath)
if (fileHandle != nil) {
fileHandle?.seekToEndOfFile()
fileHandle?.writeData(contentWithNewLine.dataUsingEncoding(NSUTF8StringEncoding)!)
}
else {
do {
try contentWithNewLine.writeToFile(filePath, atomically: true, encoding: NSUTF8StringEncoding)
} catch {
print("Error while creating \(filePath)")
}
}
}
My application (for Mac) generates some HTML... This HTML then needs to be saved to a .html file. I'm trying to use a NSSavePanel like this:
- (IBAction)saveFile:(id)sender{
NSSavePanel *savePanel = [NSSavePanel savePanel];
[savePanel setRequiredFileType:#"html"];
[savePanel setTitle:#"Save Code to File"];
if ([savePanel runModal] == NSOKButton)
{
[[_codeStore RTFFromRange:
NSMakeRange(0, [[_codeStore string] length])]
writeToURL:[savePanel URL] atomically:YES];
NSLog(#"saved");
}
}
My problem is that this does not save is a plain text. For example, when I open the generated file in a web browser, the html shows up, but
{\rtf1\ansi\ansicpg1252\cocoartf1157\cocoasubrtf700
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
\f0\fs24 \cf0
is at the top of the page...
The problem looks to be
[[_codeStore RTFFromRange: NSMakeRange(0, [[_codeStore string] length])]
writeToURL: [savePanel URL]
atomically: YES];
You're outputting an RTF document that contains the HTML. You need
[[_codeStore string] writeToURL: [savePanel URL]
atomically: YES
encoding: NSUTF8StringEncoding
error: nil];
This will take the raw NSString from your NSAttributedString _codeStore and write that to a file.
i think using rtf encoding is causing the problem since it can write beyond 8 bit using escape sequence. You may try to use simple nsstring way of writing to file.
BOOL ok = [string writeToFile:path atomically:YES
encoding:NSUnicodeStringEncoding error:&error];
hi friends
this is my xml file
1)
<sDescrizione>Crociera nei fiordi</sDescrizione>
2)
<sDescrizione>Fiat 500</sDescrizione>
3)
<sDescrizione>Orologio donna Glam sport Tissot</sDescrizione>
4)
<sDescrizione>Buoni La Rinascente 1000€</sDescrizione>
5)
<sDescrizione>Buoni Unieuro 1000€</sDescrizione>
this is what i want to retrieve from that xml file using CXML parsing method
the first 3 title are successfully retrived but when it comes at 4th it gives me error in my console like this
Entity: line 80: parser error : Input is not proper UTF-8, indicate encoding !
Bytes: 0x80 0x5D 0x5D 0x3E
<sDescrizione><![CDATA[Buoni La Rinascente 1000\200]]></sDescrizione>
^
this is my retrieving code:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CXMLDocument *xmlParser = [[[CXMLDocument alloc] initWithData:data options:0 error:nil] autorelease];
NSArray *resultNodes = [xmlParser nodesForXPath:#"//premio" error:nil];
for (CXMLElement *resultElement in resultNodes) {
for (int j=0; j<[resultElement childCount]; j++) {
NSString *tagName = [NSString stringWithString:[[resultElement childAtIndex:j] name]];
if ([tagName isEqualToString:#"sDescrizione"])
{
NSString *temp = [[resultElement childAtIndex:j] stringValue];
[catArray addObject:temp];
}
else if([tagName isEqualToString:#"idPremioSodexho"])
{
NSString *trmp = [[resultElement childAtIndex:j] stringValue];
}
}
}
Looks like the XML file is not in UTF-8 and CXMLDocument is assuming it is. When it hits the € sign its crashing. Set the correct encoding in the header of the XML file. If the XML file is encoded with ISO 8859-1 then set the header like:
<?xml version="1.0" encoding="ISO-8859-1"?>
This will allow CXMLDocument to correctly interpret the characters/codepage of your XML document.
I think you are facing the problem due to invalid Character-sets.
See, here is the line of code that you have used.
// when you don't specify any encoding, TouchXML will use NSUTF8Encoding as default
// which may lead to some problems
CXMLDocument *xmlParser = [[[CXMLDocument alloc] initWithData:data options:0 error:nil] autorelease];
// So, I recommend you to use following line of code, which may not lead you with messy situations.
CXMLDocument *doc = [[CXMLDocument alloc] initWithData:[request responseData] encoding:[request responseEncoding] options:0 error:nil];
Just curious if this is the way to do this, just want to make sure its not leaking, although I would think I am only modifying the string contents.
NSMutableString *newPath = [[NSMutableString alloc] init];
for(fileName in [manager enumeratorAtPath:rootPath]){
if ([[fileName pathExtension] isEqual:#"exr"]) {
[fileArray addObject:fileName];
// THIS BIT
[newPath setString:rootPath];
[newPath appendString:#"/"];
[newPath appendString:fileName];
// HERE
attrDir = [manager attributesOfItemAtPath:newPath error:&myError];
fileSize = [attrDir objectForKey: #"NSFileSize"];
NSLog(#"File: /%# Size: %#", fileName, fileSize);
}
}
[newPath release];
gary
This looks fine leak-wise. If you're running Xcode 3.2 you can Build->Build & Analyzer to get Clang to check this sort of thing.
Remember you only have to release things you alloc, new, copy or retain.
Consider using stringByAppendingPathComponent, rather than hardcoding the #"/" path separator. NSString has a number of methods like this specifically for working with paths.
NSString* fullPath = [rootPath stringByAppendingPathComponent:fileName];
There's nothing wrong with it, although it could be better to use initWithFormat and release:
NSString *newPath = [[NSString alloc] initWithFormat:#"%#/%#",rootPath,fileName];
// do your thing
[newPath release];
There is absolutely nothing wrong with your code, it is correct memory management.
But it can be done with even less code and memory management needed:
for(fileName in [manager enumeratorAtPath:rootPath]){
if ([[fileName pathExtension] isEqualToString:#"exr"]) {
[fileArray addObject:fileName];
NSString* newPath = [rootPath stringByAppendingPathComponent:fileName];
attrDir = [manager attributesOfItemAtPath:newPath error:&myError];
fileSize = [attrDir objectForKey: #"NSFileSize"];
NSLog(#"File: /%# Size: %#", fileName, fileSize);
}
}