如何在libzip中使用std::istream替代文件?
Great question—libzip doesn’t make this super obvious at first glance, but you can absolutely create a struct zip* directly from a std::istream without writing to the filesystem by leveraging zip sources, the library’s mechanism for working with custom data inputs.
std::istream Libzip allows you to define custom data sources via zip_source objects. You can create a source that reads directly from your std::istream using callback functions, then open that source to get your struct zip*.
Step-by-Step Implementation
Here's a complete, corrected example that fixes the issues in your original code and implements the stream-based zip source:
#include <libzip/zip.h> #include <fstream> #include <iostream> #include <stdexcept> #include <sstream> // C-style callback to read data from the std::istream static zip_int64_t stream_read_callback(void* userdata, void* buffer, zip_uint64_t len) { auto* stream = static_cast<std::istream*>(userdata); stream->read(static_cast<char*>(buffer), len); return stream->gcount(); // Return actual bytes read } // Optional callback for seeking (required for random-access zip operations) static zip_int64_t stream_seek_callback(void* userdata, zip_int64_t offset, int whence) { auto* stream = static_cast<std::istream*>(userdata); std::ios_base::seekdir dir; switch (whence) { case SEEK_SET: dir = std::ios_base::beg; break; case SEEK_CUR: dir = std::ios_base::cur; break; case SEEK_END: dir = std::ios_base::end; break; default: return -1; // Invalid seek direction } stream->seekg(offset, dir); return stream->tellg(); // Return new position } // Cleanup callback (no-op here since we don't own the stream) static void stream_cleanup_callback(void* userdata) { // If you allocated the stream dynamically, delete it here } int main() { try { // Example: Use a std::istringstream (works with any std::istream: cin, network streams, etc.) std::istringstream zip_stream(R"(/* Your raw zip data here */)"); // Create the zip source using our callbacks zip_error_t err; zip_source_t* source = zip_source_function_create( stream_read_callback, &zip_stream, stream_seek_callback, stream_cleanup_callback, &err ); if (!source) { char err_buf[256]; zip_error_to_str(err_buf, sizeof(err_buf), &err, 0); throw std::runtime_error(std::string("Failed to create zip source: ") + err_buf); } // Open the zip archive directly from the source struct zip* zip_file = zip_open_from_source(source, 0, &err); if (!zip_file) { zip_source_free(source); // Clean up source if opening fails char err_buf[256]; zip_error_to_str(err_buf, sizeof(err_buf), &err, 0); throw std::runtime_error(std::string("Failed to open zip from source: ") + err_buf); } // Extract the first file in the zip (same as your original logic, fixed) struct zip_file* file_in_zip = zip_fopen_index(zip_file, 0, 0); if (!file_in_zip) { zip_close(zip_file); throw std::runtime_error("Failed to open file inside zip archive"); } char buff[1024]; std::ofstream extracted("extracted_data"); zip_int64_t readed; while ((readed = zip_fread(file_in_zip, buff, 1024)) > 0) { extracted.write(buff, readed); } // Cleanup resources (zip_close frees the source automatically) zip_fclose(file_in_zip); zip_close(zip_file); } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; }
Key Details to Note
- Callback Roles:
stream_read_callback: Pulls data from yourstd::istreaminto libzip's buffer.stream_seek_callback: Enables random access in the zip archive (skip this if your stream doesn't support seeking, but some zip operations may fail).stream_cleanup_callback: Handles cleanup of your user data (leave empty if you manage the stream's lifecycle externally).
- Ownership: After a successful
zip_open_from_source, callingzip_close(zip_file)automatically frees the associatedzip_source_t—no need to callzip_source_freeseparately. - Error Handling: Always validate return values and use
zip_error_to_strto get human-readable error messages.
Edge Case: Non-Seekable Streams
If your std::istream doesn't support seeking (e.g., a network stream), pass nullptr instead of stream_seek_callback. Keep in mind that some zip operations (like accessing non-consecutive files) may not work, as libzip relies on seeking for random access.
内容的提问来源于stack exchange,提问作者Max Krantov

