File count of a directory in Objective-C - objective-c

I would like to know how can I get the total amount of archives inside of a directory, for example desktop.
I don't just want to know what's inside of the root of the directory, but also inside of its subfolders.
To just get the archives on the root of desktop I can do the following:
NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
NSUInteger numberOfFileInFolder = [directoryContent count];
But I need to get also the count of its subfolders.
Can somebody help me?
Edit:
Finally I have coded this way:
-(int) numberOfDocumentsInPath: (NSString *) path{
NSFileManager *manager = [[NSFileManager alloc] init];
NSDirectoryEnumerator* totalSubpaths = [manager enumeratorAtPath: path];
NSLog(#"Path %# has %d documents", path, (int)[[totalSubpaths allObjects] count]);
return (int)[[totalSubpaths allObjects] count];
}

Try this:
NSDirectoryEnumerator *subs = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:FolderPath error:nil];

Straight from the docs:
If you need to recurse into subdirectories, use
enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: as
shown in “Using a Directory Enumerator”).
Check also here:
Using a Directory Enumerator

Swift version:
var subs = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(path, error: nil) as! [String]
var filecount = subs.count
println(filecount)
for sub in subs {
//Do stuff with files
}

Related

Unable to rename the file while moving from temporary directorary

I am developing a zip extractor app i followed the algorithm that CRD explained #Here but i stuck at third step i am unable to rename the unzipped file which is at temporary directorary.
here is my code
NSURL *tempDir = [NSURL fileURLWithPath:destinationPath];
NSError *error;
NSURL *tmpDirectory = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:tempDir create:YES error:&error];
if (error) {
return ;
}
tmpDirectory = [tmpDirectory URLByAppendingPathComponent:#"extracts"];
NSLog(#"temp dir %#",tmpDirectory);
NSLog(#"temp path %#",tmpDirectory.path);
[SSZipArchive unzipFileAtPath:zipFilePath toDestination:tmpDirectory.path];
NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpDirectory.path error:nil];
NSLog(#"dir file %#",dirFiles);
for (NSString *string in dirFiles) {
NSArray *dirDestinationFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:destinationPath error:nil];
NSLog(#"dir destination file %#",dirDestinationFiles);
[dirDestinationFiles enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error;
if ([string isEqualToString:obj]) {
NSLog(#"Already present");
BOOL isMoved = [fm moveItemAtPath:tmpDirectory.path toPath:[destinationPath stringByAppendingString:[NSString stringWithFormat:#"/%#-1",string]] error:&error];
if (isMoved) {
NSLog(#"Moved");
}else{
NSLog(#"errorL %#", error);
NSLog(#"Not moved");
}
[fm removeItemAtPath:tmpDirectory.path error:&error];
[self moveFileToTrash:zipFilePath];
[self openExtractedFolderWithZipPath:zipFilePath toDestinationPath:destinationPath];
}
}];
}
Any Suggestions..
Thanks in Advance !
Let's just review your code to hopefully help you on your way.
It may seem minor, but pick good variable names:
NSURL *tempDir = [NSURL fileURLWithPath:destinationPath];
NSURL *tmpDirectory = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:tempDir create:YES error:&error];
Two names which are semantically similar for different things, that is just confusing. How about, say, destinationURL instead of tempDir?
Next, when constructing/pulling apart/etc. pathnames or URLs you will be better off being consistent. Both NSURL and NSString provide similar methods for these operations, in one place you use them:
tmpDirectory = [tmpDirectory URLByAppendingPathComponent:#"extracts"];
but then restort to direct string manipulation using a path separator which may, or may not, be correct:
[destinationPath stringByAppendingString:[NSString stringWithFormat:#"/%#-1",string]]
The routines provided by NSURL and NSString abstract away from the details of path separators and how to, say, find the extension on the last path component (which you might find useful when renaming to avoid clashes).
Going back to:
tmpDirectory = [tmpDirectory URLByAppendingPathComponent:#"extracts"];
There is no reason for you to do this. The temporary directory is created for you and you should delete it after using it. So there is no need to create a subdirectory extracts within it, and by reassigning to the same variable you've lost the URL you need to delete the temporary directory.
Now something less obvious, in my comment above I wrote:
To move each item you must handle name clashes, to do this try the move and if you get an error indicating a name clash modify the destination name however you like and re-try the move, repeating until you succeed or you until reach some limit of tries (determined by you).
I didn't explain why you should do it this way and you have tackled the problem a different way: for each item you are going to move you check for names clashes before attempting the move by iterating over the names in the destination directory.
If you read Apple's documentation on the file system you will find they often recommend you try an operation and then examine any error returned instead of trying to predict whether an error will occur and avoid it. The reason for this is the file system is dynamic, other processes can be modifying it, so if you try to avoid an error you may still get one. In pseudocode you are better of doing something like:
moveDone = false
attemptCount = 0
while not moveDone and attemptCount < MAX_ATTEMPTS
move object
if object exists error
modify destination URL
increment attemptCount
else
moveDone = true
end
end
if not moveDone then handle error
Following this outline and using a simple count and the NSString/NSURL path routines will produce you a much simpler and more reliable solution than the one you have now posted as an answer.
HTH
Here is the code working for me.
NSURL *tempDir = [NSURL fileURLWithPath:destinationPath];
NSError *error;
NSURL *tmpDirectory = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:tempDir create:YES error:&error];
if (error) {
return ;
}
tmpDirectory = [tmpDirectory URLByAppendingPathComponent:#"extracts"];
NSLog(#"temp dir %#",tmpDirectory);
NSLog(#"temp path %#",tmpDirectory.path);
[SSZipArchive unzipFileAtPath:zipFilePath toDestination:tmpDirectory.path];
NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpDirectory.path error:nil];
NSLog(#"dir file %#",dirFiles);
for (NSString *string in dirFiles) {
NSArray *dirDestinationFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:destinationPath error:nil];
NSLog(#"dir destination file %#",dirDestinationFiles);
NSMutableArray *folderCount = [[NSMutableArray alloc] init];
NSMutableArray *folderNumCount = [[NSMutableArray alloc] init];
[dirDestinationFiles enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj containsString:string]){
[folderNumCount addObject:obj];
}
if ([string isEqualToString:obj]) {
[folderCount addObject:string];
}
}];
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error;
if (folderCount.count == 0) {
NSLog(#"First time extract");
BOOL isMoved = [fm moveItemAtPath:tmpDirectory.path toPath:[destinationPath stringByAppendingString:[NSString stringWithFormat:#"/%#",string]] error:&error];
if (isMoved) {
NSLog(#"Moved");
}else{
NSLog(#"errorL %#", error);
NSLog(#"Not moved");
}
[fm removeItemAtPath:tmpDirectory.path error:&error];
// [self moveFileToTrash:zipFilePath];
// [self openExtractedFolderWithZipPath:zipFilePath toDestinationPath:destinationPath];
}else if (folderCount.count > 0){
NSLog(#"Already present");
BOOL isMoved = [fm moveItemAtPath:tmpDirectory.path toPath:[destinationPath stringByAppendingString:[NSString stringWithFormat:#"/%#-%lu",string,folderNumCount.count-1]] error:&error];
if (isMoved) {
NSLog(#"Moved");
}else{
NSLog(#"errorL %#", error);
NSLog(#"Not moved");
}
[fm removeItemAtPath:tmpDirectory.path error:&error];
// [self moveFileToTrash:zipFilePath];
// [self openExtractedFolderWithZipPath:zipFilePath toDestinationPath:destinationPath];
}
}

Cocoa contents of directory

I have a group/folder with a series of text files. I need to get a path for each one so that I can read the contents, but I can't seem to get anything to work.
I've mucked about with [NSBundle pathsForResourcesOfType:#"txt" inDirectory:#"directoryName"] which gave me nothing but nulls or a single string that reads "Contents", [[NSFileManager defaultManager] enumeratorAtPath:#"directoryName"] which I have no idea what to do with once it's created, and [[NSFileManager defaultManager] contentsOfDirectoryAtPath:#"directoryName" error:nil].
I can't figure out what I'm doing wrong, and at this point I'm just grasping at straws. I went through 20 or 30 pages on here, none of which has really helped.
I should note that this is a Cocoa Application, not iOS.
If you want to read files in arbitrary directories, the path enumerator works nicely. A bit old fashioned, but that has its charm, too.
NSString *docPath = #"/tmp";
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:docPath];
NSString *filename;
while ((filename = [dirEnum nextObject])) {
//Do something with the file name
}
If you want to read from well-known and defined directories in your home directory, then you can use NSSearchPathForDirectoriesInDomains:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths objectAtIndex: 0];
This will give you your Documents directory, and when used with the snippet above, list all files in that folder and subfolders.
Notice that we are not really supposed to use our nice, old Unix paths any more, but instead refer URLs.
In that case, you get something like:
NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *docURL = URLs[0];
NSDirectoryEnumerator *URLEnum = [[NSFileManager defaultManager] enumeratorAtURL: docURL includingPropertiesForKeys: nil options: 0 errorHandler: nil];
NSString *filename;
while ((filename = [URLEnum nextObject])) {
// ...
}
Notice that enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: has all sorts of useful parameters, which you can read about in the docs.
Lets just take 1 of your 3:
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:#"directoryName" error:nil]
You should include the error parameter and check what it contains
You need to supply a full path, not just a "directoryName"
As a result you'll get an array containing the file names of the files in the directory
So if you want the full path you can do:
NSString *directoryPath = ...;
NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:...];
for(NSString *fileName in fileNames) {
NSLog(#"%#", [directoryPath stringByAppendingPathComponent:fileName]);
}
The problem was that when adding the folder, I needed to create a reference to the folder as well. Xcode does not default to this option. I had initially chosen to simply create groups, and this does not do the job.
If your group is in your current project you can use:
NSString *path = [[NSBundle mainBundle] pathForResourcesOfType:#"txt" inDirectory:#"directoryName"]
this should work for you, I noticed that you tried something similar, but make sure you're using mainBundle.

select *.jpg file Objective-C

I am looking for a simple way in Objective-C to select all of the .jpg files in a directory. Right now I can only get all of the directory contents. Is there a way to apply a wildcard, like *.jpg, to the results?
if ( [[NSFileManager defaultManager] isReadableFileAtPath:#"/folder2/"] )
[[NSFileManager defaultManager] copyItemAtPath:#"/folder2/" toPath:#"/folder1/" error:nil];
You could use something like:
NSArray *list = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:#"/folder2/" error:nil];
for (NSString* file in list) {
if ([[file pathExtension] isEqualToString: #"jpg"]) {
[[NSFileManager defaultManager] copyItemAtPath:file toPath:#"/folder1/" error:nil];
}
}
The contentsOfDirectoryAtPath:error method returns an array containing the names of the files in the specified directory. For each entry, the result of the NSString pathExtension method is compared against the target string ("jpg"). Any matching files are copied into the destination directory.
Pretty much straight from the docs:
NSDirectoryEnumerator *dirEnum = [localFileManager enumeratorAtPath:docsDir];
NSString *file;
while (file = [dirEnum nextObject]) {
if ([[file pathExtension] isEqualToString: #"jpg"]) {
// process the document
[self doSomethingWithFile: [docsDir stringByAppendingPathComponent:file]];
}
}
It looks like NSDirectoryEnumerator also supports fast enumeration, so you could use that instead:
NSDirectoryEnumerator *dirEnum = [localFileManager enumeratorAtPath:docsDir];
for (NSString *file in dirEnum) {
if ([[file pathExtension] isEqualToString: #"doc"]) {
// process the document
[self doSomethingWithFile: [docsDir stringByAppendingPathComponent:file]];
}
}
The difference between using a directory enumerator and iterating over the list returned by -contentsOfDirectoryAtPath: is that the directory enumerator will also provide results from subdirectories.

iOS list existing directories

I already get path for documents directory and create some directories inside. I already know how to check if directory exist, delete it or its files but, how could I list directories? Thank you.
for file listing I use:
int Count;
NSString *path;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"SomeDirectoryName"];
NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
for (Count = 0; Count < (int)[directoryContent count]; Count++)
{
NSLog(#"File %d: %#", (Count + 1), [directoryContent objectAtIndex:Count]);
}
For example, this method removes all files from temporary directory of application:
- (void)cleatTmpDirectory
{
// Create a local file manager instance
NSFileManager *localFileManager = [[NSFileManager alloc] init];
NSURL *directoryToScan = [NSURL fileURLWithPath:[self applicationTmpDirectory]];
NSDirectoryEnumerator *dirEnumerator =
[[localFileManager enumeratorAtURL:directoryToScan
includingPropertiesForKeys:[NSArray arrayWithObjects:NSURLIsDirectoryKey,nil]
options: NSDirectoryEnumerationSkipsHiddenFiles |
NSDirectoryEnumerationSkipsSubdirectoryDescendants |
NSDirectoryEnumerationSkipsPackageDescendants
errorHandler:nil] retain];
NSError *error;
// Enumerate the dirEnumerator results, each value is stored in allURLs
for (NSURL *theURL in dirEnumerator)
{
// Retrieve whether a directory.
NSNumber *isDirectory;
[theURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
if ([isDirectory boolValue] == NO)
{
[localFileManager removeItemAtURL:theURL error:&error];
}
}
// Release the localFileManager.
[localFileManager release];
}
As you can find you should use NSDirectoryEnumerator *dirEnumerator and pass to its initialization method appropriate keys that you will then use.
Use the NSDirectoryEnumerator returned by NSFileManager's -enumeratorAtPath: method.

Get list of files with specific extensions at a specified path in Objective-C

I'm pretty new to Objective-C. I'm developing a Cocoa application.
Currently I'm looking for the equivalent of this C# code in Objective C:
string[] fileList = Directory.GetFiles(DownloadPath, "*.jpg");
The returned strings need not necessarily be full path, since all I need is the file names. I have tried NSFileManager but so far no good yet. Thank you.
EDIT: What I've tried with NSFileManager:
[someFileManager contentsOfDirectoryAtPath:path error:nil];
I also want to ask: what is the format of 'path'? This sounds easy, but I'm completely clueless about MAC OS file system. The path that I'm using is from [NSOpenPanel URLs], and they looks like this:
file://localhost/Users/alex/Movies/
Sometimes I get the results, but some other time The returned NSArray is just empty. I'm pretty confused about this so any help would be appreciated.
EDIT2:
The answer here: NSPredicate endswith multiple files, is probably a better choice. Nevertheless, thank you for your answer.
This code should work:
NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
NSArray *jpgFiles = [dirFiles filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self ENDSWITH '.jpg'"]];
In fact you need some thing like this
NSArray *dirFiles = [fileManager contentsOfDirectoryAtURL:[NSURL fileURLWithPath:documentsDir] includingPropertiesForKeys:[NSArray array] options:0 error:nil] ;
NSArray *filteredFiles = [dirFiles filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self.absoluteString ENDSWITH '.jpg'"]] ;
Because contentsOfDirectoryAtURL: returns array of NSURL's, so using just "self ENDSWITH '.jpg'" will crash.
maybe something along these lines:
NSFileManager * fileMan = [[NSFileManager alloc] init];
NSArray * files = [fileMan contentsOfDirectoryAtPath:#"mypath/blah" error:nil];
if (files)
{
for(int index=0;index<files.count;index++)
{
NSString * file = [files objectAtIndex:index];
if( [[file pathExtension] compare: #"jpg"] == NSOrderedSame )
{
// do something with files that end with .jpg
}
}
}
[fileMan release];