【问题标题】:Can I use ifstream in Android NDK to access Assets?我可以在 Android NDK 中使用 ifstream 来访问资产吗?
【发布时间】:2012-06-16 13:21:46
【问题描述】:

我的问题很简单,但我很难在网上找到有关此的任何信息。

是否可以使用 ifstream 从使用 Android NDK 的资产和/或资源中打开文件?

例如,将 test.txt 文件放在 /assets 中并尝试以下方法不起作用:

    char pLine[256];
    std::ifstream fin("/assets/test.txt");
    if(!fin.fail())
    {
        LOGD( "test.txt opened" );
        while( !fin.eof() )
        {
            fin.getline( pLine, 256 );
            LOGD(pLine);
        }
    }
    else
    {
        LOGD( "test.txt FAILED TO OPEN!" );
    }
    fin.close();

以下变量也没有:

    std::ifstream fin("assets/test.txt");

    std::ifstream fin("test.txt");

等等...,也没有把它放在 /res 中。

那么,是否可以使用普通的 ifstream 运算符来访问资产和/或资源文件?

【问题讨论】:

    标签: android resources assets ifstream android-ndk


    【解决方案1】:

    std::ifstream 不能使用是对的,但是可以创建一个可以以类似方式使用的assetistream。例如:

    class asset_streambuf: public std::streambuf
    {
    public:
        asset_streambuf(AAssetManager* manager, const std::string& filename)
        : manager(manager)
        {
            asset = AAssetManager_open(manager, filename.c_str(), AASSET_MODE_STREAMING);
            buffer.resize(1024);
    
            setg(0, 0, 0);
            setp(&buffer.front(), &buffer.front() + buffer.size());
        }
    
        virtual ~asset_streambuf()
        {
            sync();
            AAsset_close(asset);
        }
    
        std::streambuf::int_type underflow() override
        {
            auto bufferPtr = &buffer.front();
            auto counter = AAsset_read(asset, bufferPtr, buffer.size());
    
            if(counter == 0)
                return traits_type::eof();
            if(counter < 0) //error, what to do now?
                return traits_type::eof();
    
            setg(bufferPtr, bufferPtr, bufferPtr + counter);
    
            return traits_type::to_int_type(*gptr());
        }
    
        std::streambuf::int_type overflow(std::streambuf::int_type value) override
        {
            return traits_type::eof();
        };
    
        int sync() override
        {
            std::streambuf::int_type result = overflow(traits_type::eof());
    
            return traits_type::eq_int_type(result, traits_type::eof()) ? -1 : 0;
        }
    
    private:
        AAssetManager* manager;
        AAsset* asset;
        std::vector<char> buffer;
    };
    
    
    class assetistream: public std::istream
    {
    public:
        assetistream(AAssetManager* manager, const std::string& file)
        : std::istream(new asset_streambuf(manager, file))
        {
        }
        assetistream(const std::string& file)
        : std::istream(new asset_streambuf(manager, file))
        {
        }
    
        virtual ~assetistream()
        {
            delete rdbuf();
        }
    
        static void setAssetManager(AAssetManager* m)
        {
            manager = m;
        }
    
    private:
        static AAssetManager* manager;
    };
    
    void foo(AAssetManager* manager)
    {
        assetistream::setAssetManager(manager);
    
        assetistream as("text/tmp.txt");
        std::string s;
    
        std::getline(as, s);
    }
    

    非常欢迎改进。

    【讨论】:

    • 为什么要覆盖.sync().overflow()?为什么要存储AAssetManager?为什么使用单独的动态缓冲区而不是数组成员?顺便说一句,如果成功获取,调整该向量的大小可能会导致泄漏AAsset。您确实应该通过投掷来处理可能无法在 ctor 中打开资产的情况。并考虑使用std::unique_ptr 管理资产。
    • 感谢您的提示。但我不明白为什么不覆盖同步和溢出。因为 istream 永远不会调用它们?
    • 那个,继承的都好。
    【解决方案2】:

    不,你不能。资产存储在 apk 中,一个 zip 文件。 ifstream 无法在 zip 文件中读取。

    要访问这些文件,您需要在 java 中访问它们并将它们保存在其他位置,或者提取 apk 的内容以获取资产。

    这是一个做前者的例子。

    http://www.itwizard.ro/android-phone-installing-assets-how-to-60.html

    这是后者的一个例子。

    http://www.anddev.org/ndk_opengl_-_loading_resources_and_assets_from_native_code-t11978.html

    【讨论】:

    • 感谢您的信息。使用 AAssetManager_open 怎么样?这似乎在 C++ 中有效。
    • 我从来没有用过那个 b4(现在才听说,哈哈)。是的,看起来它应该可以工作。
    • 两个链接都坏了。
    猜你喜欢
    • 1970-01-01
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    • 2011-11-27
    • 2013-12-31
    • 1970-01-01
    • 1970-01-01
    • 2011-07-24
    相关资源
    最近更新 更多