2010-11-04 2 views
3

Я довольно долго играю в игры Valve, но я никогда не был хорош в использовании, используя массивы char для управления строками. Я ДЕЙСТВИТЕЛЬНО хотел бы улучшить, учитывая, сколько времени я трачу на них. Очевидно, что использование правильных строк было бы неплохо, но поскольку все функции SDK возвращают char * или принимают его как args, он не делает многого, потому что для преобразования назад и четвертого. У кого-нибудь есть хорошие ссылки, насколько они лучше понимают их? Большинство из того, что я нахожу в Google, просто были фрагментами.Улучшение стиля строки стиля C

Кроме того, я пытаюсь разобрать очень простой текстовый файл. В основном содержание выглядеть следующим образом ...

PatchVersion = 1.1.1.2 ProductName = л4д APPID = 440

Я хочу, чтобы получить PatchVersion и ProductName. Мой код выглядит примерно так, но на самом деле просто отсутствие надлежащего знания оставило меня в тупике. strtok только извлекает токен до знака '=', strchr дает мне указатель на его расположение, но просто не знает хорошего метода.

bool ParseSteamFile() 
{ 
    FileHandle_t file; 
    file = filesystem->Open("steam.inf", "r", "MOD"); 

    if(file) 
    { 
     int size = filesystem->Size(file); 
     char *line = new char[size + 1]; 

     while(!filesystem->EndOfFile(file)) 
     { 
      char *subLine = filesystem->ReadLine(line, size, file); 
      Msg("SUBLINE: %s\n", subLine); 

      char *buffer = ""; 

      if(strstr(subLine, "PatchVersion")) 
      { 
       char *c = strtok(subLine, "="); 
       while(c != NULL) 
       { 
        Msg("Token: %s\n", c); 
        c = strtok(subLine, "="); 
       } 
      } 
     } 
    } 
} 
+0

Не следует ли помечать этот вопрос C? – wilhelmtell

ответ

2

Нет ничего плохого в использовании C-струн. Тем не менее, вам потребуется написать довольно-очень очень низкоуровневый код, который уже был абстрагирован при использовании объектов String.

В общем, C-String представляет собой просто массив байтов (каждый байт, соответствующий значению ascii символа) с нулевым байтом в конце. Фактический синтаксис команд, однако, может быть немного эзотерическим, я рекомендовал cplusplus.com в качестве ссылки.

Ваш второй вызов strtok должен вызываться с нулем, а не подлиния снова:

 char *c = strtok(subLine, "="); 
     while(c != NULL) 
     { 
      Msg("Token: %s\n", c); 
      c = strtok(null, "="); 
     } 

На данный момент вы tokenizing от знака равенства, так что вы будете в конечном итоге с:

PatchVersion 
=1.1.1.2 ProductName 
=l4d appID 
=440 

Не забывайте, что strtok потребляет входную строку, поэтому subLine будет пуст после завершения цикла.

Прежде всего я начну с того, что вы производите каждую пару значений ключа. Затем я разделил бы каждую пару ключевых значений на ее составные элементы и сохранил бы нужные. Функция (ы) scanf может выполнять этот тип разбора очень хорошо. Для того, чтобы прочитать одну пару в name и value:

char * name = new char[255]; 
char * value = new char[255]; 
sscanf(subLine, "%s=%s", name, value); 

Вы можете затем использовать strncpy, чтобы скопировать значение в соответствующее место. sscanf не потребляет входные данные, поэтому последующие вызовы должны будут перемещать указатель subLine за пределы предыдущего совпадения (или использовать пары tr% s =% s в выражении формата вместе с тремя парами переменных имени и значения, если вы знаете всегда будет три).

Важно, чтобы ваш код приложения находился далеко от операций низкого уровня, насколько это разумно. Если вы часто делаете этот тип манипуляций, поиск или создание библиотеки, которая анализирует такие файлы, например, может быть хорошей (и забавной) идеей.

+1

+1 для общепринятых советов, хотя лучше иметь имя и значение в стеке по умолчанию, и в коде есть уязвимости переполнения буфера, которые, вероятно, не стоят представления решений, но должны быть упомянуты для записи. –

+0

Да, если какое-либо имя или значение превышают 254 символа, будут происходить плохие вещи. Не стесняйтесь выделять их по длине линии, хотя тогда их невозможно будет разместить в стеке. –

+0

OP должен остерегаться того, что 'strncpy' не делает (как правило) то, что вы намереваетесь сделать для этого. Прочтите внимательно. Я часто вижу, что это сделано неправильно в производственном коде. Стандартные функции обработки строк библиотеки C являются минной зоной вообще ... – bstpierre

2
sscanf(*subLine, "PatchVersion=%s ProductName=%s appID=%d", patchVersion, productName, &appID); 

Вы должны указать переменные appropiately, я надеюсь, что указатель вещи правильно, в противном случае отрегулировать соответствующим образом. source

+0

Это будет работать, только если пары ключ/значение находятся в этом точном порядке. –

+0

Это верно. Я просто пошел по его примеру, я думаю, что он может соответствующим образом отрегулировать этот пример и документацию. – Femaref

0

Очевидно, что использование правильных строк было бы неплохо, но поскольку все функции SDK возвращают char * или принимают его как args, это не делает многого, поскольку для преобразования назад и четвертого.

строки имеет конструктор и оператор = от сопзЬ полукокса *, так что это очень легко хранить результаты функции SDK в строках, а функция члена .c_str() позволяет результаты строк, которые передаются в качестве SDK аргументов функций. В самом деле, если ваши требования к производительности не настолько высоки, что вам нужно избегать использования кучи, std :: string стоит использовать только для того, чтобы иметь возможность автоматически расти, чтобы соответствовать данным, которые вы обрабатываете, и освобождать память, когда строка выходит за рамки. Вы, как правило, избегаете множества мелких ошибок и ограничений, используя std :: string. Вы можете кодировать строки C-стиля, используя asprintf() (если ваша система предоставляет его, а также некоторые хакеры с sprintf() и/или snprintf(), malloc(), realloc() и т. Д.) И использовать интеллектуальные указатели для получения безопасное и автоматическое освобождение памяти, но оно все еще неуклюжие. stringstreams также стоит использовать для вашего синтаксического анализа и форматирования вывода ... у них есть член .str(), и вы можете связать с ним .c_str(), чтобы получить const char *.

Если вы хотите, чтобы уровни производительности в стеке изменялись (и не против иметь верхний предел размера данных или падение на кучу для больших строк), используйте функцию sscanf(), упомянутую в других решениях. Вы уже получили хороший совет о токенизации входных данных пробелами, после чего вы можете использовать strchr для поиска первого '='.

0

Возможно, вы захотите взглянуть на библиотеку алгоритмов String Boost, которая позволяет схожие функции с тем, что std::string exposes на произвольные последовательности символов, такие как строки C.

Смежные вопросы