码迷,mamicode.com
首页 > 其他好文 > 详细

简单的INI解析封装

时间:2017-06-16 19:18:58      阅读:174      评论:0      收藏:0      [点我收藏+]

标签:gets   配置   send   eve   empty   adb   set   namespace   read   

简单封装的一个ini解析处理类(支持跨平台)。支持功能:

  1. 加载并解析指定ini文件中的配置;
  2. 读取指定 section 下的指定 key 的值。提示:支持按数值型读取,或按文本类型读取;
    使用示例:
    1 auto fWidth         = 480.0f;
    2 auto fHeight        = 320.0f;
    3 
    4 ns_ini::IniParser ini;
    5 ini.open(strConfigFile.c_str());
    6 fWidth              = ini.readFloat("glview", "screen_width", fWidth);
    7 fHeight             = ini.readFloat("glview", "screen_height", fHeight);
  3. 支持新段、项、值写入;提示:如果写入的项是已经存在的,则为修改旧项值;
    示例:
     1 auto fWidth         = 480.0f;
     2 auto fHeight        = 320.0f;
     3 
     4 ns_ini::IniParser ini;
     5 ini.open(strConfigFile.c_str());
     6 fWidth              = ini.readFloat("glview", "screen_width", fWidth);
     7 fHeight             = ini.readFloat("glview", "screen_height", fHeight);
     8 
     9 ini.writeFloat("glview", "screen_height, 777.77f); // 将旧值修改为 777.77f
    10 ini.writeInt("glview", "screen_height, 666); // 再次将旧值修改为 666
    11 ini.writeText("glview", "screen_height, "jacc.kim"); // 再次将旧值修改为 文本"jacc.kim"

源码如下:

技术分享
 1 /******************************************************************************
 2 
 3                              I‘m jacc.kim
 4 
 5     CreateDate: 2017-06-13 17:12:16 
 6     FileName  : JKIniParser.h
 7     Version   : 1.00
 8     Author    : jacc.kim
 9     Summary   : ini parser wrapper
10 
11 ******************************************************************************/
12 #pragma once
13 
14 #include <list>
15 #include <map>
16 
17 namespace ns_ini
18 {
19 
20 class Section;
21 
22 /******************************************************************************
23  * create   : (jacc.kim) [06-13-2017]
24  * summary  : class IniParser
25 ******************************************************************************/
26 class IniParser
27 {
28 public:
29     void                                        setIniFile(const char* const pcszIniFile);
30     bool                                        open(const char* const pcszIniFile = nullptr);
31     bool                                        save(const char* const pcszIniFile = nullptr);
32     void                                        close();
33 
34     unsigned int                                getSectionAmount() const;
35     bool                                        isSectionExisted(const char* const pcszSection) const;
36     bool                                        isParameterExisted(const char* const pcszSection, const char* const pcszParameter) const;
37 
38     int                                         readInt(const char* const pcszSection, const char* const pcszKey, const int def = 0) const;
39     unsigned int                                readUnsignedInt(const char* const pcszSection, const char* const pcszKey, const unsigned int def = 0u) const;
40     bool                                        readBool(const char* const pcszSection, const char* const pcszKey, const bool def = false) const;
41     float                                       readFloat(const char* const pcszSection, const char* const pcszKey, const float def = 0.0f) const;
42     double                                      readDouble(const char* const pcszSection, const char* const pcszKey, const double def = 0.0) const;
43     const char*                                 readText(const char* const pcszSection, const char* const pcszKey, const char* const def = nullptr) const;
44 
45     // !!!note: write existed section & key, the old value will be replaced.
46     void                                        writeInt(const char* const pcszSection, const char* const pcszKey, const int value);
47     void                                        writeUnsignedInt(const char* const pcszSection, const char* const pcszKey, const unsigned int value);
48     void                                        writeBool(const char* const pcszSection, const char* const pcszKey, const bool value);
49     void                                        writeFloat(const char* const pcszSection, const char* const pcszKey, const float value);
50     void                                        writeDouble(const char* const pcszSection, const char* const pcszKey, const double value);
51     void                                        writeText(const char* const pcszSection, const char* const pcszKey, const char* const value);
52 
53     bool                                        remove(const char* const pcszSection, const char* const pcszKey);
54     bool                                        remove(const char* const pcszSection);
55     void                                        clear();
56 
57 public:
58     IniParser();
59     ~IniParser();
60 
61     Section*                                    getSection(const char* const pcszSection) const;
62     Section&                                    operator[](const char* const pcszSection) const;
63 
64 private:
65     IniParser(const IniParser& rhs)/* = delete*/;
66     IniParser& operator=(const IniParser& rhs)/* = delete*/;
67 
68     void                                        trimLeft(char*& p, char*& q);
69     void                                        trimRight(char*& p, char*& q);
70     void                                        trim(char*& p, char*& q);
71     void                                        trimRN0(char*& p, char*& q);
72     bool                                        isEndWithN(const char* p, const size_t nLength);
73     bool                                        stringEqual(const char* p, const char* q, int nChar = INT_MAX) const;
74     bool                                        isSectionName(char* p, char* q);
75     char*                                       splitKeyValue(char* p, char* q);
76     void                                        parseIniLine(char* szIniLine);
77     void                                        addSection(char* szSectionName);
78     void                                        addParameter(char* szKey, char* szValue);
79 
80 private:
81     typedef std::list<Section*>                 SectionList;
82     typedef SectionList::iterator               SectionListIter;
83 
84     // key = section name, value = section‘s ptr
85     typedef std::map <std::string, SectionListIter> SectionMap;
86 
87 private:
88     std::string                                 m_strIniFile;       // ini file name
89     SectionList                                 m_listSections;     // all sections.
90     SectionMap                                  m_mapSectionName;   // section <--> index mapping.(only reference.)
91 
92 };//class IniParser
93 
94 }//namespace ns_ini
IniParser.h 文件
技术分享
  1 #include "JKIniParser.h"
  2 
  3 namespace ns_ini
  4 {
  5 
  6 /******************************************************************************
  7  * create   : (jacc.kim) [06-13-2017]
  8  * summary  : class Section
  9 ******************************************************************************/
 10 class Section
 11 {
 12 public:
 13     struct Parameter
 14     {
 15         std::string                             key;
 16         std::string                             value;
 17     };//struct Parameter
 18 
 19     typedef std::list<Parameter>                ParameterList;
 20     typedef ParameterList::iterator             ParameterListIter;
 21     typedef std::map<std::string, ParameterListIter>    ParameterMap;
 22 
 23 public:
 24     Section(const char* pcszSectionName) : m_strSectionName  (pcszSectionName)
 25                                          , m_listParameters  ()
 26                                          , m_mapParameterKeys()
 27     {}
 28 
 29     ~Section() {
 30         this->clear();
 31     }
 32 
 33     const char* getSectionName() const {
 34         return m_strSectionName.c_str();
 35     }
 36 
 37     bool isParameterExisted(const char* const pcszKey) const {
 38         return m_mapParameterKeys.find(pcszKey) != m_mapParameterKeys.end();
 39     }
 40 
 41     Parameter& operator[](const char* const pcszKey) const {
 42         auto iter                               = m_mapParameterKeys.find(pcszKey);
 43         return *(iter->second);
 44     }
 45 
 46     const char* read(const char* const pcszKey) {
 47         const char* pcszRetValue                = nullptr;
 48         if (nullptr != pcszKey) {
 49             auto iter                           = m_mapParameterKeys.find(pcszKey);;
 50             if (m_mapParameterKeys.end() != iter) {
 51                 pcszRetValue                    = (*(iter->second)).value.c_str();
 52             }
 53         }
 54         return pcszRetValue;
 55     }
 56 
 57     bool write(const char* const pcszKey, const char* const pcszValue) {
 58         auto iter = m_mapParameterKeys.find(pcszKey);
 59         if (m_mapParameterKeys.end() == iter) {
 60             Parameter param = { pcszKey, pcszValue };
 61             m_listParameters.emplace_back(param);
 62             m_mapParameterKeys[pcszKey] = --m_listParameters.end();
 63             return true;
 64         }
 65         (*this)[pcszKey].value                  = pcszValue;
 66         return true;
 67     }
 68 
 69     bool remove(const char* pcszKey) {
 70         auto bIsSuccess                         = false;
 71         if (nullptr != pcszKey) {
 72             auto iter                           = m_mapParameterKeys.find(pcszKey);
 73             if (m_mapParameterKeys.end() != iter) {
 74                 m_listParameters.erase(iter->second);
 75                 m_mapParameterKeys.erase(iter);
 76                 bIsSuccess                      = true;
 77             }
 78         }
 79         return bIsSuccess;
 80     }
 81 
 82     void clear() {
 83         m_listParameters.clear();
 84         m_mapParameterKeys.clear();
 85     }
 86 
 87     ParameterList& getAllParameters() {
 88         return m_listParameters;
 89     }
 90 
 91 private:
 92     std::string                                 m_strSectionName;
 93     ParameterList                               m_listParameters;
 94     ParameterMap                                m_mapParameterKeys;
 95 
 96 };//class Section
 97 
 98 ///////////////////////////////////////////////////////////////////////////////
 99 // class IniParser
100 IniParser::IniParser() : m_strIniFile    ("")
101                        , m_listSections  ()
102                        , m_mapSectionName()
103 {
104 
105 }
106 
107 IniParser::~IniParser() {
108     this->clear();
109 }
110 
111 void IniParser::setIniFile(const char* const pcszIniFile) {
112     if (nullptr == pcszIniFile) {
113         return;
114     }
115     m_strIniFile                    = pcszIniFile;
116 }
117 
118 bool IniParser::open(const char* const pcszIniFile/* = nullptr*/) {
119     auto bIsSuccess                 = false;
120     const char* pcszOpenFile        = nullptr != pcszIniFile ? pcszIniFile : m_strIniFile.c_str();
121     if (nullptr == pcszOpenFile) {
122         return bIsSuccess;
123     }
124     if (nullptr != pcszIniFile) {
125         this->setIniFile(pcszIniFile);
126     }
127     this->clear();
128     auto pINIFile                   = fopen(pcszIniFile, "rb");
129     if (nullptr != pINIFile) {
130         bIsSuccess = true;
131         const size_t nBUFF_SIZE     = 2048;
132         char szBuff[nBUFF_SIZE];
133         while (!feof(pINIFile)) {
134             memset((void*)szBuff, 0x0L, nBUFF_SIZE);
135             fgets(szBuff, nBUFF_SIZE, pINIFile);
136             this->parseIniLine(szBuff);
137         }
138         fclose(pINIFile);
139         pINIFile                    = nullptr;
140     }
141 
142     return bIsSuccess;
143 }
144 
145 bool IniParser::save(const char* const pcszIniFile/* = nullptr*/) {
146     auto bIsSuccess                 = false;
147     const char* pcszSaveFile        = nullptr != pcszIniFile ? pcszIniFile : m_strIniFile.c_str();
148     if (nullptr == pcszSaveFile) {
149         return bIsSuccess;  // 没有可保存的路径
150     }
151     ::remove(pcszSaveFile);
152     auto pINIFile                   = fopen(pcszSaveFile, "ab+");
153     if (nullptr != pINIFile) {
154         bIsSuccess                  = true;
155         auto iter                   = m_listSections.begin();
156         const auto iterend          = m_listSections.end();
157         const size_t nBUFF_SIZE     = 2048;
158         char szBuffer[nBUFF_SIZE];
159         Section* pSection           = nullptr;
160         for (; iterend != iter;/* ++iter*/) {
161             pSection                = *iter;
162             const char* pcszSectionName = pSection->getSectionName();
163             fputs("[", pINIFile);
164             fputs(pcszSectionName, pINIFile);
165             fputs("]\r\n", pINIFile);
166             auto& allParameters     = (*iter)->getAllParameters();
167             auto listiter           = allParameters.begin();
168             const auto listiterend  = allParameters.end();
169             for (; listiterend != listiter; ++listiter) {
170                 auto& param         = *listiter;
171                 memset((void*)szBuffer, 0x0L, nBUFF_SIZE);
172                 memcpy(szBuffer, param.key.c_str(), param.key.length());
173                 szBuffer[param.key.length()] = =;
174                 memcpy(szBuffer + param.key.length() + 1, param.value.c_str(), param.value.length());
175                 const auto nEndPos    = param.key.length() + 1 + param.value.length();
176                 szBuffer[nEndPos + 1] = \0;
177                 fputs(szBuffer, pINIFile);
178                 if (!this->isEndWithN(szBuffer, nEndPos + 1)) {
179                     fputs("\r\n", pINIFile);
180                 }
181             }
182             if (iterend != ++iter) {
183                 fputs("\r\n", pINIFile);
184             }
185         }
186         fclose(pINIFile);
187         pINIFile = nullptr;
188     }
189 
190     return bIsSuccess;
191 }
192 
193 void IniParser::close() {
194     this->save();
195     this->clear();
196     this->setIniFile(nullptr);
197 }
198 
199 unsigned int IniParser::getSectionAmount() const {
200     return static_cast<unsigned int>(m_listSections.size());
201 }
202 
203 bool IniParser::isSectionExisted(const char* const pcszSection) const {
204     return nullptr != this->getSection(pcszSection);
205 }
206 
207 bool IniParser::isParameterExisted(const char* const pcszSection, const char* const pcszParameter) const {
208     auto pSection = this->getSection(pcszSection);
209     return nullptr != pSection && pSection->isParameterExisted(pcszParameter);
210 }
211 
212 int IniParser::readInt(const char* const pcszSection, const char* const pcszKey, const int def/* = 0*/) const {
213     int retValue                    = def;
214     const char* pcszText            = this->readText(pcszSection, pcszKey, nullptr);
215     if (nullptr != pcszText) {
216         if (1 != sscanf(pcszText, "%d", &retValue)) {
217             retValue                = def; // 读取失败.
218         }
219     }
220     return retValue;
221 }
222 
223 unsigned int IniParser::readUnsignedInt(const char* const pcszSection, const char* const pcszKey, const unsigned int def/* = 0u*/) const {
224     unsigned int retValue           = def;
225     const char* pcszText            = this->readText(pcszSection, pcszKey, nullptr);
226     if (nullptr != pcszText) {
227         if (1 != sscanf(pcszText, "%u", &retValue)) {
228             retValue                = def; // 读取失败.
229         }
230     }
231     return retValue;
232 }
233 
234 bool IniParser::readBool(const char* const pcszSection, const char* const pcszKey, const bool def/* = false*/) const {
235     bool retValue                   = def;
236     const char* pcszText            = this->readText(pcszSection, pcszKey, nullptr);
237     if (nullptr != pcszText) {
238         int iTempVal = 0;
239         if (1 == sscanf(pcszText, "%d", &iTempVal)) {
240             return 0 == iTempVal ? false : true;
241         } else if (this->stringEqual(pcszText, "true")) {
242             return true;
243         } else {
244             return false;
245         }
246     }
247     return retValue;
248 }
249 
250 float IniParser::readFloat(const char* const pcszSection, const char* const pcszKey, const float def/* = 0.0f*/) const {
251     float retValue                  = def;
252     const char* pcszText            = this->readText(pcszSection, pcszKey, nullptr);
253     if (nullptr != pcszText) {
254         if (1 != sscanf(pcszText, "%f", &retValue)) {
255             retValue                = def; // 读取失败.
256         }
257     }
258     return retValue;
259 }
260 
261 double IniParser::readDouble(const char* const pcszSection, const char* const pcszKey, const double def/* = 0.0*/) const {
262     double retValue                 = def;
263     const char* pcszText            = this->readText(pcszSection, pcszKey, nullptr);
264     if (nullptr != pcszText) {
265         if (1 != sscanf(pcszText, "%lf", &retValue)) {
266             retValue                = def; // 读取失败.
267         }
268     }
269     return retValue;
270 }
271 
272 const char* IniParser::readText(const char* const pcszSection, const char* const pcszKey, const char* const def/* = nullptr*/) const {
273     const char* pcszRetValue        = def;
274     auto pSection                   = this->getSection(pcszSection);
275     if (nullptr != pSection) {
276         pcszRetValue                = pSection->read(pcszKey);
277     }
278     return pcszRetValue;
279 }
280 
281 void IniParser::writeInt(const char* const pcszSection, const char* const pcszKey, const int value) {
282     const size_t nBUFF_SIZE         = 256;
283     char szBuff[nBUFF_SIZE];
284     memset((void*)szBuff, 0x0L, nBUFF_SIZE);
285     sprintf(szBuff, "%d", value);
286     this->writeText(pcszSection, pcszKey, szBuff);
287 }
288 
289 void IniParser::writeUnsignedInt(const char* const pcszSection, const char* const pcszKey, const unsigned int value) {
290     const size_t nBUFF_SIZE         = 256;
291     char szBuff[nBUFF_SIZE];
292     memset((void*)szBuff, 0x0L, nBUFF_SIZE);
293     sprintf(szBuff, "%u", value);
294     this->writeText(pcszSection, pcszKey, szBuff);
295 }
296 
297 void IniParser::writeBool(const char* const pcszSection, const char* const pcszKey, const bool value) {
298     const size_t nBUFF_SIZE         = 256;
299     char szBuff[nBUFF_SIZE];
300     memset((void*)szBuff, 0x0L, nBUFF_SIZE);
301     sprintf(szBuff, "%d", value ? 1 : 0);
302     this->writeText(pcszSection, pcszKey, szBuff);
303 }
304 
305 void IniParser::writeFloat(const char* const pcszSection, const char* const pcszKey, const float value) {
306     const size_t nBUFF_SIZE         = 256;
307     char szBuff[nBUFF_SIZE];
308     memset((void*)szBuff, 0x0L, nBUFF_SIZE);
309     sprintf(szBuff, "%g", value);
310     this->writeText(pcszSection, pcszKey, szBuff);
311 }
312 
313 void IniParser::writeDouble(const char* const pcszSection, const char* const pcszKey, const double value) {
314     const size_t nBUFF_SIZE         = 256;
315     char szBuff[nBUFF_SIZE];
316     memset((void*)szBuff, 0x0L, nBUFF_SIZE);
317     sprintf(szBuff, "%g", value);
318     this->writeText(pcszSection, pcszKey, szBuff);
319 }
320 
321 void IniParser::writeText(const char* const pcszSection, const char* const pcszKey, const char* const value) {
322     if (nullptr != pcszSection && nullptr != pcszKey && nullptr != value) {
323         auto iter                   = m_mapSectionName.find(pcszSection);
324         Section* pSection           = nullptr;
325         if (m_mapSectionName.end() == iter) {
326             pSection                = new (std::nothrow) Section(pcszSection);
327             if (nullptr == pSection) {
328                 return;
329             }
330             m_listSections.emplace_back(pSection);
331             m_mapSectionName[pcszSection] = --m_listSections.end();
332         } else {
333             pSection                = *(iter->second);
334         }
335         pSection->write(pcszKey, value);
336     }
337 }
338 
339 bool IniParser::remove(const char* const pcszSection, const char* const pcszKey) {
340     auto bIsSuccess                 = false;
341     if (nullptr != pcszSection) {
342         auto iter                   = m_mapSectionName.find(pcszSection);
343         if (m_mapSectionName.end() != iter) {
344             auto pSection           = *(iter->second);
345             if (nullptr != pSection) {
346                 bIsSuccess          = pSection->remove(pcszKey);
347             }
348         }
349     }
350     return bIsSuccess;
351 }
352 
353 bool IniParser::remove(const char* const pcszSection) {
354     auto bIsSuccess                 = false;
355     if (nullptr != pcszSection) {
356         auto iter                   = m_mapSectionName.find(pcszSection);
357         if (m_mapSectionName.end() != iter) {
358             auto list_iter          = iter->second;
359             Section* pSection       = *list_iter;
360             if (nullptr != pSection) {
361                 delete pSection;
362                 pSection            = nullptr;
363             }
364             m_listSections.erase(list_iter);
365             m_mapSectionName.erase(iter);
366             bIsSuccess              = true;
367         }
368     }
369     return bIsSuccess;
370 }
371 
372 void IniParser::clear() {
373     auto iter                       = m_listSections.begin();
374     const auto iterend              = m_listSections.end();
375     Section* pSection               = nullptr;
376     for (; iterend != iter; ++iter) {
377         pSection                    = *iter;
378         if (nullptr != pSection) {
379             delete pSection;
380             pSection                = nullptr;
381         }
382     }
383     m_listSections.clear();
384     m_mapSectionName.clear();
385 }
386 
387 Section* IniParser::getSection(const char* const pcszSection) const {
388     Section* pRetSection            = nullptr;
389     auto iter                       = m_mapSectionName.find(pcszSection);
390     if (m_mapSectionName.end() != iter) {
391         pRetSection                 = *(iter->second);
392     }
393     return pRetSection;
394 }
395 
396 Section& IniParser::operator[](const char* const pcszSection) const {
397     auto iter                       = m_mapSectionName.find(pcszSection);
398     return *(*(iter->second));
399 }
400 
401 void IniParser::trimLeft(char*& p, char*& q) {
402     if (nullptr != p) {
403         while (  == *p && ++p <= q);
404     }
405 }
406 
407 void IniParser::trimRight(char*& p, char*& q) {
408     if (nullptr != q) {
409         while (  == *q && --q >= p);
410     }
411 }
412 
413 void IniParser::trim(char*& p, char*& q) {
414     this->trimLeft(p, q);
415     this->trimRight(p, q);
416 }
417 
418 void IniParser::trimRN0(char*& p, char*& q) {
419     if (nullptr != q) {
420         while ((\0 == *q || \r == *q || \n == *q) && --q >= p);
421     }
422 }
423 
424 bool IniParser::isEndWithN(const char* p, const size_t nLength) {
425     if (0 == nLength) {
426         return false;
427     }
428     const char* q = p + nLength;
429     while (\0 == *q && --q >= p);
430     return (q >= p) && (\n == *q);
431 }
432 
433 bool IniParser::stringEqual(const char* p, const char* q, int nChar/* = INT_MAX*/) const {
434     int n = 0;
435     if (p == q) {
436         return true;
437     }
438     while (*p && *q && *p == *q && n < nChar) {
439         ++p;
440         ++q;
441         ++n;
442     }
443     if ((n == nChar) || (*p == 0 && *q == 0)) {
444         return true;
445     }
446     return false;
447 }
448 
449 bool IniParser::isSectionName(char* p, char* q) {
450     if (nullptr == p || nullptr == q) {
451         return false;
452     }
453     return [ == *p && ] == *q;
454 }
455 
456 char* IniParser::splitKeyValue(char* p, char* q) {
457     char* pRetEqualSignPos = p;
458     while (= != *pRetEqualSignPos && ++pRetEqualSignPos < q);
459     return pRetEqualSignPos;
460 }
461 
462 void IniParser::parseIniLine(char* szIniLine) {
463     char* p = szIniLine;
464     char* q = p + strlen(p);
465     this->trimRN0(p, q);
466     this->trim(p, q);
467     if (p < q) {
468         // 只有这种情况才需要处理.因为 p > q,肯定不需要处理,说明全都是空格.如果p = q也不需要处理,因为只有一个字符还需要处理什么?
469         *(q + 1) = \0;
470         if (this->isSectionName(p, q)) {
471             *q = \0;
472             ++p;
473             this->addSection(p);
474         } else {
475             char* szEqualSignPos = this->splitKeyValue(p, q);
476             if (szEqualSignPos > p && szEqualSignPos < q) {
477                 *szEqualSignPos = \0;
478                 ++szEqualSignPos;
479                 this->addParameter(p, szEqualSignPos);
480             }
481         }
482     }
483 }
484 
485 void IniParser::addSection(char* szSectionName) {
486     auto iter                       = m_mapSectionName.find(szSectionName);
487     if (m_mapSectionName.end() != iter) {
488         // 已存在
489         auto listiter               = iter->second;
490         m_listSections.emplace_back(*listiter);
491         m_listSections.erase(listiter);
492         m_mapSectionName.erase(iter);
493         m_mapSectionName[szSectionName] = --m_listSections.end();
494         return;
495     }
496     Section* pSection               = new (std::nothrow) Section(szSectionName);
497     if (nullptr == pSection) {
498         return;
499     }
500     m_listSections.emplace_back(pSection);
501     m_mapSectionName[szSectionName] = --m_listSections.end();
502 }
503 
504 void IniParser::addParameter(char* szKey, char* szValue) {
505     if (m_listSections.empty()) {
506         return;
507     }
508     auto pSection                   = m_listSections.back();
509     pSection->write(szKey, szValue);
510 }
511 
512 }//namespace ns_ini
IniParser.cpp 文件

如果对该封装有改进或不当之处,欢迎指定、交流!

简单的INI解析封装

标签:gets   配置   send   eve   empty   adb   set   namespace   read   

原文地址:http://www.cnblogs.com/tongy0/p/7028726.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!