Токенизация строк в C
Я пытаюсь обозначить строку, используя пробел, как разделитель, но это не работает. Есть ли у кого-нибудь предложение о том, почему он не работает?
Изменить: токенизация с использованием:
strtok(string, " ");
Код выглядит следующим образом
pch = strtok (str," ");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ");
}
Ответы
Ответ 1
Сделайте это так:
char s[256];
strcpy(s, "one two three");
char* token = strtok(s, " ");
while (token) {
printf("token: %s\n", token);
token = strtok(NULL, " ");
}
Примечание: strtok
изменяет строку на ее токенизацию, поэтому она не может быть const char*
.
Ответ 2
Здесь пример использования strtok
, помните, что strtok
разрушает его входную строку (и поэтому никогда не может использоваться в строковой константе
char *p = strtok(str, " ");
while(p != NULL) {
printf("%s\n", p);
p = strtok(NULL, " ");
}
В основном следует отметить, что передача NULL
в качестве первого параметра в strtok
говорит ему, чтобы он получил следующий токен из строки, которая была ранее добавлена в токенизацию.
Ответ 3
strtok может быть очень опасным. Он не является потокобезопасным. Его предполагаемое использование должно вызываться снова и снова в цикле, передавая результат с предыдущего вызова. Функция strtok имеет внутреннюю переменную, которая хранит состояние вызова strtok. Это состояние не уникально для каждого потока - оно глобально. Если какой-либо другой код использует strtok в другом потоке, у вас возникают проблемы. Не те проблемы, которые вы хотите отслеживать!
Я бы рекомендовал искать реализацию регулярного выражения или использовать sscanf, чтобы разделить строку.
Попробуйте следующее:
char strprint[256];
char text[256];
strcpy(text, "My string to test");
while ( sscanf( text, "%s %s", strprint, text) > 0 ) {
printf("token: %s\n", strprint);
}
Примечание. Строка "текст" уничтожается по мере ее разделения. Это не может быть предпочтительным поведением =)
Ответ 4
Вы можете упростить код, введя дополнительную переменную.
#include <string.h>
#include <stdio.h>
int main()
{
char str[100], *s = str, *t = NULL;
strcpy(str, "a space delimited string");
while ((t = strtok(s, " ")) != NULL) {
s = NULL;
printf(":%s:\n", t);
}
return 0;
}
Ответ 5
Я сделал некоторые строковые функции, чтобы разделить значения, используя меньшее количество указателей, поскольку я мог, потому что этот код предназначен для работы на процессорах PIC18F. Эти процессоры не очень хорошо справляются с указателями, когда у вас мало свободной памяти:
#include <stdio.h>
#include <string.h>
char POSTREQ[255] = "pwd=123456&apply=Apply&d1=88&d2=100&pwr=1&mpx=Internal&stmo=Stereo&proc=Processor&cmp=Compressor&ip1=192&ip2=168&ip3=10&ip4=131&gw1=192&gw2=168&gw3=10&gw4=192&pt=80&lic=&A=A";
int findchar(char *string, int Start, char C) {
while((string[Start] != 0)) { Start++; if(string[Start] == C) return Start; }
return -1;
}
int findcharn(char *string, int Times, char C) {
int i = 0, pos = 0, fnd = 0;
while(i < Times) {
fnd = findchar(string, pos, C);
if(fnd < 0) return -1;
if(fnd > 0) pos = fnd;
i++;
}
return fnd;
}
void mid(char *in, char *out, int start, int end) {
int i = 0;
int size = end - start;
for(i = 0; i < size; i++){
out[i] = in[start + i + 1];
}
out[size] = 0;
}
void getvalue(char *out, int index) {
mid(POSTREQ, out, findcharn(POSTREQ, index, '='), (findcharn(POSTREQ, index, '&') - 1));
}
void main() {
char n_pwd[7];
char n_d1[7];
getvalue(n_d1, 1);
printf("Value: %s\n", n_d1);
}
Ответ 6
При чтении документации strtok я вижу, что вам нужно передать указатель NULL после первого вызова "инициализации". Может быть, вы этого не сделали. Конечно, конечно.
Ответ 7
Вот еще одна реализация strtok()
, которая имеет возможность распознавать последовательные разделители (стандартная библиотека strtok()
не имеет этого)
Эта функция является частью лицензированной библиотеки BSD, называемой zString. Вы более чем можете внести свой вклад:)
https://github.com/fnoyanisi/zString
char *zstring_strtok(char *str, const char *delim) {
static char *static_str=0; /* var to store last address */
int index=0, strlength=0; /* integers for indexes */
int found = 0; /* check if delim is found */
/* delimiter cannot be NULL
* if no more char left, return NULL as well
*/
if (delim==0 || (str == 0 && static_str == 0))
return 0;
if (str == 0)
str = static_str;
/* get length of string */
while(str[strlength])
strlength++;
/* find the first occurance of delim */
for (index=0;index<strlength;index++)
if (str[index]==delim[0]) {
found=1;
break;
}
/* if delim is not contained in str, return str */
if (!found) {
static_str = 0;
return str;
}
/* check for consecutive delimiters
*if first char is delim, return delim
*/
if (str[0]==delim[0]) {
static_str = (str + 1);
return (char *)delim;
}
/* terminate the string
* this assignmetn requires char[], so str has to
* be char[] rather than *char
*/
str[index] = '\0';
/* save the rest of the string */
if ((str + index + 1)!=0)
static_str = (str + index + 1);
else
static_str = 0;
return str;
}
Как упоминалось в предыдущих сообщениях, поскольку strtok()
или тот, который я вложил выше, полагается на переменную static *char
, чтобы сохранить местоположение последнего разделителя между последовательными вызовами, следует проявлять особую осторожность при работе с многопоточными мки.
Ответ 8
int not_in_delimiter(char c, char *delim){
while(*delim != '\0'){
if(c == *delim) return 0;
delim++;
}
return 1;
}
char *token_separater(char *source, char *delimiter, char **last){
char *begin, *next_token;
char *sbegin;
/*Get the start of the token */
if(source)
begin = source;
else
begin = *last;
sbegin = begin;
/*Scan through the string till we find character in delimiter. */
while(*begin != '\0' && not_in_delimiter(*begin, delimiter)){
begin++;
}
/* Check if we have reached at of the string */
if(*begin == '\0') {
/* We dont need to come further, hence return NULL*/
*last = NULL;
return sbegin;
}
/* Scan the string till we find a character which is not in delimiter */
next_token = begin;
while(next_token != '\0' && !not_in_delimiter(*next_token, delimiter)) {
next_token++;
}
/* If we have not reached at the end of the string */
if(*next_token != '\0'){
*last = next_token--;
*next_token = '\0';
return sbegin;
}
}
void main(){
char string[10] = "abcb_dccc";
char delim[10] = "_";
char *token = NULL;
char *last = "" ;
token = token_separater(string, delim, &last);
printf("%s\n", token);
while(last){
token = token_separater(NULL, delim, &last);
printf("%s\n", token);
}
}
Вы можете прочитать подробный анализ в блоге, указанном в моем профиле:)