3

Background

I'm working on an app that calls a library my company has written previously in (plain) C. The main part of my app is written in Obj-C. At one point in the C code, the library needs to open and read data from a .txt file. The .txt file will be created by the Obj-C in the main part of the app.


The Issue

I can't get the C code to find the .txt file. Specifically, when i try to fopen the file and assign it to a FILE variable, the variable is always NULL. Right now, the file is being written to the Documents folder of my app (but if that's the problem then I can change it). I've enabled file sharing, so I was able to verify with iTunes that the file is being written correctly and exists in the Documents folder.


My Attempts

Here's some of the code I've attempted so far.

In App:

NSURL *docs = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
const char *dir = [docs UTF8String];
myLibraryEntryPoint(dir,(int)[docs length]);

In Library:

char *new_str;
if ((new_str = malloc(strlen(directory)+strlen("/myFile.txt")+1)) != NULL)
{
    new_str[0] = '\0';
    strcat(new_str,directory);
    strcat(new_str,"/myFile.txt");
}
else
{
    fprintf(stderr, "malloc failed!");
}
FILE *fp=fopen(new_str, "r");

I've also tried the following in the library:

FILE *fp=fopen("Documents/myFile.txt", "r");
FILE *fp=fopen("/Documents/myFile.txt", "r");
FILE *fp=fopen("myFile.txt", "r");
FILE *fp=fopen("/myFile.txt", "r");
FILE *fp=fopen("Documents\myFile.txt", "r");
FILE *fp=fopen("Documents\\myFile.txt", "r");
FILE *fp=fopen("\Documents\myFile.txt", "r");
FILE *fp=fopen("\\Documents\\myFile.txt", "r");

As I said above, fp is always NULL after each of these attempts. What is the correct syntax in c to open a file in an iOS environment? I haven't made any changes in my Build Settings for my project to change the working directory, as covered in all of these questions, because unlike those questions, my app is creating the .txt file itself.


Resolution

Turns out file paths are case sensitive. My library was looking for myFile.TXT, where my app was writing to myFile.txt. FML.

I'm going to leave this question here, because I think there's some good information in the comments and the suggested answers about file path syntax and how the current directory is handled by the library. That information could be useful to others in the future.

Community
  • 1
  • 1
GeneralMike
  • 2,951
  • 3
  • 28
  • 56
  • 1
    Since you're handling relative paths, could you try to print the current directory? `puts(getcwd());` IIRC. – Jean-François Fabre Sep 16 '16 at 13:59
  • Don't trial&error, but use a debugger and use an absolute path to the file. `/myFile.txt` is an absolute path, but most likely not the one the file is located! And provide a [mcve]. – too honest for this site Sep 16 '16 at 14:08
  • @Jean-FrançoisFabre: I should put `puts(getcwd());` in my C code library, right? If i just type that line, it throws a compiler error - the `getcwd` part doesn't show up in the autocomplete, so I wonder if that isn't supported? – GeneralMike Sep 16 '16 at 14:14
  • Try that with the include line: `#include char *getcwd(char *buf, size_t size);` But maybe it's not supported. – Jean-François Fabre Sep 16 '16 at 14:17
  • @Jean-FrançoisFabre: Yep, I needed that include line - thanks. It just prints out `/`. So that means the working directory **isn't** the directory where my app is installed, so all the relative paths I tried won't work, correct? – GeneralMike Sep 16 '16 at 14:25
  • 1
    @GeneralMike: yes, it seems that your process starts with root dir as current directory. You have to chdir to your app directory or provide the full absolute path to it. – Jean-François Fabre Sep 16 '16 at 14:30
  • @Jean-FrançoisFabre, @Olaf: so the leading `\ ` makes it an absolute path in C? Good to know. In that case, the first example I provided in `My Attempts` should be sending a `char[]` with the absolute path into the library, and using that to try to open the file - it still doesn't work though. I'll focus on that option then. – GeneralMike Sep 16 '16 at 14:30
  • not `\\`, `/`. you're on unix :) – Jean-François Fabre Sep 16 '16 at 14:35

3 Answers3

1

The path to Documentsdirectory is accessible like this in objective-C:

NSString *documentsPath = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES)[0];

When using such a NSString path in C, you have to convert your NSString to UTF8 string:

NSString *fullPath = [documentPath stringByAppendingPathComponent:@"myFile.txt"];
File *f = fopen(fullPath.UTF8String, "r");

Be aware that fopen() C function need to be passed the absolute path to the desired file to be able to open it.

If fopen() returns NULL, you can check what went wrong by checking errno for the error number and strerror() function (from ) to have a human readable explanation:

#include <stdio.h>
#include <errno.h>
#include <string.h>

File *fileHandler = fopen( fullPathToFileAsUTF8String, openMode );
if( fileHandler == NULL ) {
    fprintf(stderr, "File open error: %s\n", strerror(errno));
}
Nicolas Buquet
  • 3,880
  • 28
  • 28
  • Isn't this the same thing as what I'm doing in the first part I listed under `My Attempts`? And the library is a .c file, which means I can't have any obj-c code in that file. – GeneralMike Sep 16 '16 at 14:16
  • C string is not the same as UT8 string. – Nicolas Buquet Sep 16 '16 at 14:41
  • I'm using `UTF8String`. I tried using `NSSearchPathForDirectoriesInDomains`, doesn't look like it changed anything. – GeneralMike Sep 16 '16 at 14:50
  • So, perhaps your file created by your application is not closed. Try to open a file that you created by hand using iTunes File Sharing. – Nicolas Buquet Sep 16 '16 at 14:55
  • That was a good idea. I copied the file, added a `2` to the end of the name, put it back in with iTunes, and changed the code in just the library to look for the `2` file, but it still didn't find it. – GeneralMike Sep 16 '16 at 15:21
  • Can you copy here a full path to the file as computed by your program? – Nicolas Buquet Sep 16 '16 at 15:28
  • And you can use C `errno` variable and `strerror()`function to get the error message corresponding to the last call of `fopen()` : `strerror(errno)` – Nicolas Buquet Sep 16 '16 at 16:03
  • Turns out it wasn't working for me because of a typo. You were on the right track with a lot of your suggestions. If you want to put something in your answer that explicitly mentions the C library needs to look for the file using the absolute directory, I'll mark this as accepted, as I feel that's the part that's likely to be the most useful to others in the future. – GeneralMike Sep 16 '16 at 16:13
  • I completed my explanation as asked :-) – Nicolas Buquet Sep 16 '16 at 16:32
0

I think the problem with your first attempt is that you are passing a URL to a library function that expects a path. Try:

NSURL *docs = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSString *docsPath = docs.path;
const char *dir = [docsPath UTF8String];
myLibraryEntryPoint(dir,(int)[docsPath length]);
Jim Matthews
  • 1,181
  • 8
  • 16
0

Was having a similar issue. I have a library in C from where I need to read-write files.

I wrote the code below to find the path to the Documents folder of the app:

char path[256] = {0};
strcpy(path, getenv("HOME"));
strcpy(path, "/Documents/file.txt");
FILE *file = fopen(path, "r");
Daniel
  • 2,380
  • 29
  • 44