I want to parse a .csv file. For this I use the CHCSV Parser. But when I push into the view where the parser should start parsing, the app crashes.
Terminating app due to uncaught exception 'NSMallocException', reason:
'* -[NSConcreteMutableData appendBytes:length:]: unable to allocate
memory for length (4294967295)'
NSString *filePath = #"http://somewhere.com/test.csv";
NSString *fileContent = [NSString stringWithContentsOfURL:[NSURL URLWithString:filePath] encoding:NSUTF8StringEncoding error:nil];
self.csvParser = [[CHCSVParser alloc] initWithContentsOfCSVFile:fileContent];
Edit:
I'm developing for iOS 6+. Thanks for the great comments and answers. I hope to get the right solution.
Input Stream
It doesn't work. When I want to work with the input stream the app crashes because of the wrong encoding.
Incompatible integer to pointer conversion sending 'int' to
parameter of type 'NSStringEncoding *' (aka 'unsigned int *')
NSData *downloadData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://example.com/test.csv"]];
NSInputStream *stream = [NSInputStream inputStreamWithData:downloadData];
self.csvParser = [[CHCSVParser alloc] initWithInputStream:stream usedEncoding:NSUTF8StringEncoding delimiter:#";"];
self.csvParser.delegate = self;
[self.csvParser parse];
CSV-String
NSString *filePath = #"http://example.com/test.csv";
NSString *fileContent = [NSString stringWithContentsOfURL:[NSURL URLWithString:filePath] encoding:NSUTF8StringEncoding error:nil];
self.csvParser = [[CHCSVParser alloc] initWithCSVString:fileContent];
self.csvParser.delegate = self;
[self.csvParser parse];
This parse only (null).
Final Edit: Dave, the author of CHCSVParser, updated his code on github, so this problem should be solved when you use the most recent version. Get it now!
Okay, here we go:
First add the following code in CHCSVParser.m:
In method - (void)_sniffEncoding at the very beginning you have:
uint8_t bytes[CHUNK_SIZE];
NSUInteger readLength = [_stream read:bytes maxLength:CHUNK_SIZE];
[_stringBuffer appendBytes:bytes length:readLength];
[self setTotalBytesRead:[self totalBytesRead] + readLength];
change it to:
uint8_t bytes[CHUNK_SIZE];
NSUInteger readLength = [_stream read:bytes maxLength:CHUNK_SIZE];
if (readLength > CHUNK_SIZE) {
readLength = CHUNK_SIZE;
}
[_stringBuffer appendBytes:bytes length:readLength];
[self setTotalBytesRead:[self totalBytesRead] + readLength];
After that changed I got only null values so I changed the file path (in the sample project it is located in the main(), however I did the parsing in viewDidLoad.
Make sure you copied the file in your bundle directory for that to work!
file = [NSBundle pathForResource:#"Test" ofType:#"scsv" inDirectory:[[NSBundle mainBundle] bundlePath]];
Edit:
When you say you need to download the file you can do following (but notice that this is quick and dirty solution especially on mobile devices)
NSData *downloadData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.yourdomain.tld/Test.scsv"]];
NSInputStream *stream = [NSInputStream inputStreamWithData:downloadData];
The last line is the important one here you need to change.
Hope that solves your issue.
Edit 2:
I've just created a repository with a demo project for you where the code actually works. Perhaps you can find out what you do wrong (or at least different). Here is the link.
Edit 3:
Change
self.csvParser = [[CHCSVParser alloc] initWithInputStream:stream usedEncoding:NSUTF8StringEncoding delimiter:#";"];
to
self.csvParser = [[CHCSVParser alloc] initWithInputStream:stream usedEncoding:&encoding delimiter:';'];
Related
I would like to parse csv from webserver which gets updated everyday.I am using the csvparser from this link https://github.com/davedelong/CHCSVParser and I am using this code:
NSError *err = [[[NSError alloc] init] autorelease];
NSString *lunchFileURL = [[NSString stringWithFormat:#"http://www.somewhere.com/LunchSpecials.csv"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *lunchFile = [NSString stringWithContentsOfURL:[NSURL URLWithString:lunchFileURL] encoding:NSUTF8StringEncoding error:&err];
CHCSVParser *p = [[CHCSVParser alloc] initWithContentsOfCSVString:lunchFile usedEncoding:&encoding error:nil];
I get this error :
No visible #interface for 'CHCSVParser' declares the selector 'initWithContentsOfCSVString:usedEncoding:error:'
I checked this link Load remote csv into CHCSVParser and its not working .I am a noob to ios ,Please let me know how to fix this .Really Appreciate the help.Thanks in Advance.
It should probably be:
NSError *err;
NSString *lunchFileURL = [[NSString stringWithFormat:#"http://www.somewhere.com/LunchSpecials.csv"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *lunchFile = [NSString stringWithContentsOfURL:[NSURL URLWithString:lunchFileURL]
encoding:NSUTF8StringEncoding
error:&err];
// if you're going to create a CHCSVParser yourself, the syntax is:
CHCSVParser *p = [[CHCSVParser alloc] initWithCSVString:lunchFile
encoding:NSUTF8StringEncoding
error:&err];
// or, if you're going to use NSArray+CHCSVAdditions.h, the syntax might be:
NSArray *array = [[NSArray alloc] initWithContentsOfCSVString:lunchFile
encoding:NSUTF8StringEncoding
error:&err];
Note:
You don't need to alloc/init the NSError object; these classes that take a NSError ** parameter will create the autorelease error object for you if they encounter an error; otherwise they leave it alone;
The CHCSVParser class method is not initWithContentsOfCSVString, but rather initWithCSVString;
Alternatively, if you use the NSArray class extension, then the syntax is arrayWithContentsOfCSVString or initWithContentsOfCSVString; and
You specified an encoding of &encoding, but this parameter is not a pointer, so I don't see how that can possibly be right; I've just specified the encoding.
I assume you want to use the NSArray+CHCSVAdditions category method initWithContentsOfCSVString or arrayWithContentsOfCSVString (which gives you an autorelease object), not the CHCSVParser, but it's up to you.
I am kind of new to iOS development.
I want to retrieve strings from the text(.rtf) file I have. The file is within my application main bundle. It's content are:
#start word1 First word end word2 Second word end //lots of things to be added later
Code:
path = [[NSBundle mainBundle]pathForResource:#"words" ofType:#"rtf"];
if(path)
{
NSLog(#"path exists");
}
NSError *error = nil;
NSString *file = [[NSString alloc]initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
if(error)
{
NSLog(#"error");
}
NSString *finalword= [[NSString alloc]init ];
NSString *startfrom = [[NSString alloc] initWithFormat:#"word%i",i+1];
i++;
NSLog(#"%#",startfrom);
NSString *wordoftheday = [[NSString alloc]init ];
NSScanner *scanner = [NSScanner scannerWithString:file];
[scanner scanUpToString:startfrom intoString:nil];
[scanner scanUpToString:#"end" intoString:&wordoftheday];
finalword = [wordoftheday substringFromIndex:[startfrom length]];
NSLog(#"%#",finalword);
Word.text = final word; //change label text
//[final word release];
//[wordoftheday release];
//[file release];
Code is working fine but it leaves me with memory management issues. App crashes if I release the variables in the last commented code.
Also this method is in my viewdidload. I want the label to change text when user click a button. I will have to write the same code again in that method which leaves me with more memory issue.
So looking at these one by one, focusing on the memory issues and not the overall strategy here:
NSString *finalword= [[NSString alloc]init ];
Here you alloc/init a new immutable and empty NSString, and then you end up overwriting the pointer to this later anyway. You should just delete this line. And then you'll need to move the declaration down a few lines to this:
NSString *finalword = [wordoftheday substringFromIndex:[startfrom length]];
Then you have:
NSString *startfrom = [[NSString alloc] initWithFormat:#"word%i",i+1];
This one you need to release later. Or just change it to:
NSString *startfrom = [NSString stringWithFormat:#"word%i",i+1];
Then you have:
NSString *wordoftheday = [[NSString alloc]init ];
Same story as finalword. Except that you do need to define this variable so you can pass it to the scanner later. So change it to:
NSString *wordoftheday = nil;
And lastly, you can release 'file'. That is fine. But you don't want to release 'wordoftheday' or 'finalword' because you don't own those strings. You did not create them yourself.
And one other note:
if(error)
That is not the correct way to check for an error in loading 'file'. You should check the return from the method and then look for an error if and only if the return value was nil. So change that line to:
if(!file)
(OK, that wasn't really a memory issue, but a bug I did notice.)
I think that's all of it at least as far as memory issues. I hope that helps.
make those variables as member variables and release in the dealloc
I have a problem with the next code:
NSDictionary * imagen = [[NSDictionary alloc] initWithDictionary:[envio resultValue]];
NSString *imagenS = [imagen valueForKey:#"/Result"];
ClaseMaestra *b1 = [[ClaseMaestra alloc]init];
NSData *imagenDecode = [[NSData alloc] initWithData:[b1 base64DataFromString:imagenS]];
NSLog(#"Decode Image:");
NSLog(#"%#", imagenDecode);
//SAVE IMAGE
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *docDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#david.png",docDirectory];
[imagenDecode writeToFile:filePath atomically:YES];
Blockquote
[envio resultValue] --> return a NSDictionary with one image in Base 64 codification.
I want decoder and save this image but in my console I have showed this message:
2011-08-23 19:19:39.750 WSStub[38501:a0f] *************************
2011-08-23 19:19:39.752 WSStub[38501:a0f] SendImage
2011-08-23 19:19:39.752 WSStub[38501:a0f] *************************
2011-08-23 19:19:39.759 WSStub[38501:a0f] -[ClaseMaestra base64DataFromString:]: unrecognized selector sent to instance 0xd00ad0
Program received signal: “EXC_BAD_ACCESS”.
ClaseMaestra interface is:
#import <Foundation/Foundation.h>
#class NSString;
#interface ClaseMaestra : NSObject
+ (NSMutableData *)base64DataFromString: (NSString *)string;
#end
I can´t understand the "unrecognized selector" error...
This is a class method and you call iton an instance of the class. You should either change it to an instance method. instead of:
+ (NSMutableData *)base64DataFromString: (NSString *)string;
Use:
- (NSMutableData *)base64DataFromString: (NSString *)string;
Or, change the call, instead of:
NSData *imagenDecode = [[NSData alloc] initWithData:[b1 base64DataFromString:imagenS]];
Use:
NSData *imagenDecode = [[NSData alloc] initWithData:[ClaseMaestra base64DataFromString:imagenS]];
What to choose depends on your needs.
base64DataFromString: is a class method (starts with a +). So instead of
ClaseMaestra *b1 = [[ClaseMaestra alloc]init];
NSData *imagenDecode = [[NSData alloc] initWithData:[b1 base64DataFromString:imagenS]];
You should do
NSData *data = [ClaseMaestra base64DataFromString:imagenS];
You are sending a class message to an instance. The receiver should be a class.
So do:
NSData *imagenDecode = [[NSData alloc] initWithData:[ClaseMaestra base64DataFromString:imagenS]];
You'll also get this error if you use the name of a private framework, eg: MPMovieView .Everyone knows you're not supposed to use those, but what I didn't know is that I was using one!
What's odd is, if you use Xibs, they load the system one and give you the same type of error (Class methods).
But if you load it in code, it shadows the system framework one. I spent a decent hour scratching my head, ensuring everything was hooked up right... it was, just needed to change how I named my custom stuff. Posting this for anyone with similar
I am using Dave DeLong's CHCSVParser to parse a csv. I can parse the csv locally, but I cannot get it load a remote csv file. I have been staring at my MacBook way too long today and the answer is right in front of me. Here is my code:
NSString *urlStr = [[NSString alloc] initWithFormat:#"http://www.somewhere.com/LunchSpecials.csv"];
NSURL *lunchFileURL = [NSURL URLWithString:urlStr];
NSStringEncoding encoding = 0;
CHCSVParser *p = [[CHCSVParser alloc] initWithContentsOfCSVFile:[lunchFileURL path] usedEncoding:&encoding error:nil];
[p setParserDelegate:self];
[p parse];
[p release];
Thanks for any help that someone can give me.
-[NSURL path] is not doing what you're expecting.
If I have the URL http://stackoverflow.com/questions/4636428, then it's -path is /questions/4636428. When you pass that path to CHCSVParser, it's going to try and open that path on the local system. Since that file doesn't exist, you won't be able to open it.
What you need to do (as Walter points out) is download the CSV file locally, and then open it. You can download the file in several different ways (+[NSString stringWithContentsOfURL:...], NSURLConnection, etc). Once you've got either the file saved locally to disk or the string of CSV in memory, you can then pass it to the parser.
If this is a very big file, then you'll want to alloc/init a CHCSVParser with the path to the local copy of the CSV file. The parser will then read through it bit by bit and tell you what it finds via the delegate callbacks.
If the CSV file isn't very big, then you can do:
NSString * csv = ...; //the NSString containing the contents of the CSV file
NSArray * rows = [csv CSVComponents];
That will return an NSArray of NSArrays of NSStrings.
Similar to this last approach is using the NSArray category method:
NSString * csv = ...;
NSError * error = nil;
NSArray * rows = [NSArray arrayWithContentsOfCSVString:csv encoding:[csv fastestEncoding] error:&error];
This will return the same structure (an NSArray of NSArrays of NSStrings), but it will also provide you with an NSError object if it encounters a syntax error in the CSV file (ie, malformed CSV).
I think you need an NSString, not an NSURL object to pass to the parser so the extra part you are doing with changing the NSString to an NSURL is the issue. Looking at the CHCSVParser documentation, it looks like he wants NSString in the init.
So maybe you could do something like:
NSError *err = [[[NSError alloc] init] autorelease];
NSString *lunchFileURL = [[NSString stringWithFormat:#"http://www.somewhere.com/LunchSpecials.csv"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *lunchFile = [NSString stringWithContentsOfURL:[NSURL URLWithString:lunchFileURL] encoding:NSUTF8StringEncoding error:&err];
CHCSVParser *p = [[CHCSVParser alloc] initWithContentsOfCSVString:lunchFile usedEncoding:&encoding error:nil];
I have some source code to get the file name of an url
for example:
http://www.google.com/a.pdf
I hope to get a.pdf
because the way to join 2 NSStrings I can get is 'appendString' which only for adding a string at right side, so I planned to check each char one by one from the right side of string 'http://www.google.com/a.pdf', when it reach at the char '/', stop the checking, return string fdp.a , after that I change fdp.a to a.pdf
source codes are below
-(NSMutableString *) getSubStringAfterH : originalString:(NSString *)s0
{
NSInteger i,l;
l=[s0 length];
NSMutableString *h=[[NSMutableString alloc] init];
NSMutableString *ttt=[[NSMutableString alloc] init ];
for(i=l-1;i>=0;i--) //check each char one by one from the right side of string 'http://www.google.com/a.pdf', when it reach at the char '/', stop
{
ttt=[s0 substringWithRange:NSMakeRange(i, 1)];
if([ttt isEqualToString:#"/"])
{
break;
}
else
{
[h appendString:ttt];
}
}
[ttt release];
NSMutableString *h1=[[[NSMutableString alloc] initWithFormat:#""] autorelease];
for (i=[h length]-1;i>=0;i--)
{
NSMutableString *t1=[[NSMutableString alloc] init ];
t1=[h substringWithRange:NSMakeRange(i, 1)];
[h1 appendString:t1];
[t1 release];
}
[h release];
return h1;
}
h1 can reuturn the coorect string a.pdf, but if it returns to the codes where it was called, after a while system reports
'double free
*** set a breakpoint in malloc_error_break to debug'
I checked a long time and foudn that if I removed the code
ttt=[s0 substringWithRange:NSMakeRange(i, 1)];
everything will be Ok (of course getSubStringAfterH can not returns the corrent result I expected.), no error reported.
I try to fix the bug a few hours, but still no clue.
Welcome any comment
Thanks
interdev
The following line does the job if url is a NSString:
NSString *filename = [url lastPathComponent];
If url is a NSURL, then the following does the job:
NSString *filename = [[url path] lastPathComponent];
Try this:
Edit: from blow comment
NSString *url = #"http://www.google.com/a.pdf";
NSArray *parts = [url componentsSeparatedByString:#"/"];
NSString *filename = [parts lastObject];
I think if you have already had the NSURL object, there is lastPathComponent method available from the iOS 4 onwards.
NSURL *url = [NSURL URLWithString:#"http://www.google.com/a.pdf"];
NSString *filename = [url lastPathComponent];
Swift 3
Let's say that your url is http://www.google.com/a.pdf
let filename = url.lastPathComponent
\\filename = "a.pdf"
This is more error free and meant for getting the localized name in the URL.
NSString *localizedName = nil;
[url getResourceValue:&localizedName forKey:NSURLLocalizedNameKey error:NULL];
I haven't tried this yet, but it seems like you might be trying to do this the hard way. The iPhone libraries have the NSURL class, and I imagine that you could simply do:
NSString *url = [NSURL URLWithString:#"http://www.google.com/a.pdf"];
NSString *path = [url path];
Definitely look for a built in function. The libraries have far more testing and will handle the edge cases better than anything you or I will write in an hour or two (generally speaking).