Windows 程序移植到Linux平台的一波N折

本文记录了一位开发者将Windows上的HTML文件分析器移植到Linux Ubuntu过程中的遭遇,包括安装Ubuntu Desktop版、解决依赖问题以及编译环境的搭建。在遇到apt-get install build-essential依赖问题后,通过使用aptitude成功解决并完成了编译。

 本人前端时间写了个HTML文件分析器,windows平台上的VC6.0,使用标准C++。昨天接到指令,应要求要发布一个Linux版本的,茫然啦,Linux下面的hello world都没有玩过,对linux下的C++程序仅仅限于听他们神吹过的GCC, G++, MAKEFILE什么的,其他一概不知。这就注定移植过程必然full of frustrating.

 

早上在如同涓涓溪流的网络中下了个600多M的Ubuntu ISO,装上虚拟机,安装Ubuntu,一切顺利,安装成功,重新启动,大汗.只有黑乎乎的命令行,没有图像界面儿。哎,原来装的是Ubuntu server 版。

 

重新睁大眼睛下载了个Ubuntu Destop Edition ubuntu-9.10-desktop-i386.iso,还是在官网上下的,还是目前最新版,安装到58%的时候出现Failed to copy file之类的话,反复尝试了几次还是不行。只得再次下载了ubuntu-8.04.3-desktop-i386.iso。还好,这次很顺利,很快的安装成功了。

 

ubuntu-8.04.3-desktop基本上是裸体,什么有用的软件都没有。恩。好像有GCC。但是想用GCC完成对HTML PARSER(用了stl)的编译是不得行的,必须要G++,以及相关库。在网上很容易查到“sudo apt-get install build-essential”之类的方法。我尝试了,不行,就提示写什么什么依赖什么的。又到官网上下了build-essential.deb,双击运行,死,还是说依赖什么什么东东没有,失败;沉住气,把依赖的××.deb下载下来,运行,xx.deb有依赖××.deb,...........,无语到了极点,怎么发现循环依赖呢!!!! 这个问题到现在还不清楚,哪个可以告诉我?请留言。

 

下午经论坛高人甲指点:

1.把盘放进光驱
2.$sudo apt-cdrom add
3.$sudo apt-get install build-essential

高人

<think>我们正在将WindowsAPI中的`GetPrivateProfileString`函数移植Linux平台。该函数的功能是从INI配置文件中读取指定节(section)和键(key)对应的字符串值。###实现思路1.**读取整个INI文件**:将文件内容读入内存。2.**解析INI文件结构**:-查找指定的节(section)-在找到的节中查找指定的键(key)-提取键对应的值(value)3.**处理未找到的情况**:如果未找到节或键,则返回默认值。###代码实现(手动解析INI文件)以下是个C++实现,它不依赖外部库,适用于轻量级使用。```cpp#include<fstream>#include<string>#include<algorithm>#include<cctype>//forstd::isspace//去除字符串两端的空白字符std::stringtrim(conststd::string&str){size_tstart=str.find_first_not_of("\t");if(start==std::string::npos)return"";size_tend=str.find_last_not_of("\t");returnstr.substr(start,end-start+1);}//实现GetPrivateProfileString功能std::stringGetPrivateProfileString(conststd::string&section,conststd::string&key,conststd::string&defaultValue,conststd::string&filename){std::ifstreaminFile(filename);if(!inFile.is_open()){returndefaultValue;//文件无法打开,返回默认值}std::stringcurrentSection;std::stringline;boolinTargetSection=false;while(std::getline(inFile,line)){//去除行首尾空白std::stringtrimmedLine=trim(line);//跳过空行和注释行(以分号或井号开头)if(trimmedLine.empty()||trimmedLine[0]==';'||trimmedLine[0]=='#'){continue;}//检查是否是节(section)if(trimmedLine.front()=='['&&trimmedLine.back()==']'){//提取节名(去掉括号)currentSection=trim(trimmedLine.substr(1,trimmedLine.size()-2));//判断是否是目标节inTargetSection=(currentSection==section);}//如果在目标节中,检查键值对elseif(inTargetSection){//查找等号分隔符size_tpos=trimmedLine.find('=');if(pos!=std::string::npos){//提取键和值std::stringcurrentKey=trim(trimmedLine.substr(0,pos));std::stringcurrentValue=trim(trimmedLine.substr(pos+1));//如果找到目标键,返回值if(currentKey==key){inFile.close();returncurrentValue;}}}}inFile.close();returndefaultValue;//未找到,返回默认值}```###使用示例```cppintmain(){std::stringvalue=GetPrivateProfileString("Database","ConnectionString","default_connection","config.ini");std::cout<<"读取到的配置值:"<<value<<std::endl;return0;}```###替代方案:使用inih库对于更复杂的应用,推荐使用轻量级INI解析库[inih](https://github.com/benhoyt/inih)(仅需单个头文件)。```cpp#include"ini.h"#include<cstring>//回调函数,用于查找指定键的值intvalue_handler(void*user,constchar*section,constchar*name,constchar*value){//用户数据:我们传递个结构体,包含我们要查找的节和键,以及结果struct{constchar*target_section;constchar*target_key;std::stringresult;}*ctx=static_cast<decltype(ctx)>(user);//如果节和键匹配,则记录值if(strcmp(section,ctx->target_section)==0&&strcmp(name,ctx->target_key)==0){ctx->result=value;}return1;//继续解析}std::stringGetPrivateProfileString(constchar*section,constchar*key,constchar*defaultValue,constchar*filename){//初始化上下文struct{constchar*target_section;constchar*target_key;std::stringresult;}ctx={section,key,defaultValue};//解析INI文件if(ini_parse(filename,value_handler,&ctx)<0){//文件解析错误,返回默认值returndefaultValue;}returnctx.result;}```###性能优化考虑根据引用[2]的优化建议,如果频繁读取INI文件,可以考虑以下优化:1.**缓存INI文件内容**:避免重复读取和解析文件。2.**减少内存访问开销**:确保数据结构紧凑(例如使用连续内存存储键值对)。3.**避免字符串复制**:使用字符串视图(C++17的`std::string_view`)来引用原始数据。###跨平台兼容性在跨平台项目中,可以封装个适配层:```cpp#ifdef_WIN32#include<Windows.h>#else//使用上述Linux实现#endifstd::stringGetConfigString(conststd::string&section,conststd::string&key,conststd::string&defaultValue){#ifdef_WIN32wchar_tresult[256];DWORDlen=GetPrivateProfileStringW(std::wstring(section.begin(),section.end()).c_str(),std::wstring(key.begin(),key.end()).c_str(),L"",result,sizeof(result)/sizeof(result[0]),std::wstring(filename.begin(),filename.end()).c_str());return(len>0)?std::string(result,result+len):defaultValue;#elsereturnGetPrivateProfileString(section,key,defaultValue,filename);#endif}```###注意事项1.**文件编码**:Windows通常使用本地编码(如GBK),而Linux推荐UTF-8。如果需要跨平台共享配置文件,建议统使用UTF-8编码。2.**文件路径**:在Linux中,配置文件通常存储在`/etc/`(系统级)或用户主目录(`~/.config/`)下。3.**错误处理**:如果文件不存在或无法读取,应返回默认值(与Windows行为致)。###相关问题1.如何在Linux中实现`WritePrivateProfileString`的对应功能?2.INI文件在Linux系统的最佳存储位置是什么?3.如何正确处理INI文件中的注释和特殊字符?4.在多线程环境中如何安全地读写INI文件?</think># 在Linux上实现Windows `GetPrivateProfileString`功能 在Linux环境中实现Windows API函数`GetPrivateProfileString`的功能,需要理解该函数的核心作用:**从INI格式配置文件中读取指定节(section)和键(key)对应的字符串值**。下面我将提供完整的实现方案和代码示例。 ## 实现方案分析 `GetPrivateProfileString`的核心功能: - 从INI文件中读取指定节(section)和键(key)的值 - 支持默认值参数(当键不存在时返回) - 自动处理空白字符和注释 - 支持不同编码格式(Windows通常使用本地编码,Linux推荐UTF-8) Linux下的替代方案: 1. **手动解析INI文件**:轻量级实现,适合简单需求 2. **使用INI解析库**:如`inih`或`cpp-httplib`,更健壮 3. **使用系统配置机制**:如GNOME的GConf/dconf(不符合INI标准) ## 手动解析实现(C++) ```cpp #include <fstream> #include <string> #include <algorithm> #include <cctype> // 去除字符串首尾空白字符 std::string trim(const std::string& str) { auto start = str.find_first_not_of(" \t\r\n"); if (start == std::string::npos) return ""; auto end = str.find_last_not_of(" \t\r\n"); return str.substr(start, end - start + 1); } // 实现GetPrivateProfileString功能 std::string GetPrivateProfileString( const std::string& section, const std::string& key, const std::string& defaultValue, const std::string& filename) { std::ifstream inFile(filename); if (!inFile.is_open()) { return defaultValue; // 文件无法打开,返回默认值 } std::string currentSection; std::string line; bool inTargetSection = false; while (std::getline(inFile, line)) { // 去除行首尾空白 std::string trimmedLine = trim(line); // 跳过空行和注释行(以;或#开头) if (trimmedLine.empty() || trimmedLine[0] == ';' || trimmedLine[0] == '#') { continue; } // 检查是否是节(section) if (trimmedLine.front() == '[' && trimmedLine.back() == ']') { // 提取节名(去掉方括号) currentSection = trim(trimmedLine.substr(1, trimmedLine.size() - 2)); // 检查是否为目标节 inTargetSection = (currentSection == section); } // 如果在目标节中,检查键值对 else if (inTargetSection) { // 查找等号分隔符 size_t pos = trimmedLine.find('='); if (pos != std::string::npos) { // 提取键名 std::string currentKey = trim(trimmedLine.substr(0, pos)); // 检查是否为目标键 if (currentKey == key) { // 提取键值 std::string value = trim(trimmedLine.substr(pos + 1)); inFile.close(); return value; } } } } inFile.close(); return defaultValue; // 未找到键值,返回默认值 } ``` ## 使用示例 ```cpp int main() { // 读取配置值 std::string connStr = GetPrivateProfileString( "Database", "ConnectionString", "Server=localhost;Port=3306;", "config.ini" ); std::cout << "数据库连接字符串: " << connStr << std::endl; // 读取不存在的键值 std::string timeout = GetPrivateProfileString( "Settings", "Timeout", "30", "config.ini" ); std::cout << "超时设置: " << timeout << "秒" << std::endl; return 0; } ``` ## 使用inih库实现(更健壮方案) [inih](https://github.com/benhoyt/inih)是个轻量级INI解析库,适合需要更健壮解析的情况: ```cpp #include "ini.h" #include <cstring> #include <string> // 回调函数,用于查找指定键的值 int value_handler(void* user, const char* section, const char* name, const char* value) { // 用户数据结构:包含目标节、键和结果 struct { const char* target_section; const char* target_key; std::string result; } *ctx = static_cast<decltype(ctx)>(user); // 检查是否匹配目标节和键 if (strcmp(section, ctx->target_section) == 0 && strcmp(name, ctx->target_key) == 0) { ctx->result = value; } return 1; // 继续解析 } std::string GetPrivateProfileString( const char* section, const char* key, const char* defaultValue, const char* filename) { // 初始化上下文 struct { const char* target_section; const char* target_key; std::string result = defaultValue; // 默认为defaultValue } ctx = { section, key, defaultValue }; // 解析INI文件 if (ini_parse(filename, value_handler, &ctx) < 0) { // 文件解析错误,返回默认值 return defaultValue; } return ctx.result; } ``` 编译时需要链接inih库: ```bash g++ -o program program.cpp -lini ``` ## 性能优化考虑 根据引用[2]的建议[^2],优化内存访问性能: - **缓存配置数据**:避免重复解析文件 - **紧凑数据结构**:减少缓存未命中 - **批量读取**:减少文件I/O次数 ```cpp class ConfigCache { private: std::unordered_map<std::string, std::string> cache; std::string filename; time_t lastModified = 0; public: ConfigCache(const std::string& file) : filename(file) {} std::string get(const std::string& section, const std::string& key, const std::string& defaultValue) { // 检查文件是否修改 struct stat fileInfo; if (stat(filename.c_str(), &fileInfo) == 0) { if (fileInfo.st_mtime > lastModified) { reload(); lastModified = fileInfo.st_mtime; } } // 构建缓存键:section.key std::string cacheKey = section + "." + key; auto it = cache.find(cacheKey); return (it != cache.end()) ? it->second : defaultValue; } private: void reload() { // 重新加载并解析INI文件 cache.clear(); // ... 解析代码 ... } }; ``` ## 跨平台兼容实现 在跨平台项目中,可以创建统接口: ```cpp #ifdef _WIN32 #include <Windows.h> #else // 包含上述Linux实现 #endif std::string GetConfigString( const std::string& section, const std::string& key, const std::string& defaultValue, const std::string& filename) { #ifdef _WIN32 wchar_t result[256]; // Windows实现 DWORD len = GetPrivateProfileStringW( std::wstring(section.begin(), section.end()).c_str(), std::wstring(key.begin(), key.end()).c_str(), L"", result, sizeof(result)/sizeof(result[0]), std::wstring(filename.begin(), filename.end()).c_str() ); return (len > 0) ? std::string(result, result+len) : defaultValue; #else // Linux实现 return GetPrivateProfileString(section, key, defaultValue, filename); #endif } ``` ## 最佳实践建议 1. **文件位置**: - 系统级配置:`/etc/appname/config.ini` - 用户级配置:`~/.config/appname/config.ini` 2. **编码处理**: ```cpp // 转换到UTF-8(如果需要) std::string utf8Str = convert_to_utf8(rawStr); ``` 3. **特殊字符处理**: - 支持带引号的值:`Key = "value with spaces"` - 转义特殊字符:`\n`, `\t`, `\\`, `\"` 4. **多线程安全**: ```cpp #include <mutex> static std::mutex configMutex; std::string GetConfigValue(...) { std::lock_guard<std::mutex> lock(configMutex); // 安全访问 } ``` ## 注意事项 1. **文件权限**:确保应用程序对配置文件有读取权限 2. **错误处理**:增加详细的错误日志记录 3. **性能监控**:对于高频访问,监控解析性能 4. **配置变更通知**:实现文件变更监听(如inotify)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值