0

假设我有一个不变的 c 风格字符串说

const char* msg = "fred,jim,345,7665";

我想对此进行标记并读出各个字段,但出于性能原因,我不想复制。我怎样才能做到这一点?

显然 strtok 需要一个非常量指针,而 boost::tokenizer 是一个选项,但我不确定幕后在做什么。

4

5 回答 5

1

不可避免地,您将需要该字符串的一些副本,即使它是被复制的子字符串。

如果你有一个 strtok_r 函数,你可以使用它,但它仍然需要一个可变字符串来完成它的工作。但是请注意,并非所有系统都提供该功能(例如 Windows),这就是我在这里提供实现的原因。它通过需要一个附加参数来工作:一个指向 C 字符串的指针以保存下一个匹配的地址。这允许它在理论上更具可重入性(线程安全)。但是,您仍然会改变值。如果您愿意,您可以修改它以满足您的需要,也许将 N 个字节复制到目标缓冲区并以空值终止该缓冲区以避免修改源字符串的需要。

/*
   Usage:
     char *tok;
     char *savep;
     tok = mystrtok_r (somestr, ",", &savep);
     while (NULL != tok)
       {
         /* Do something with `tok'.  */
         tok = mystrtok_r (NULL, ",", &savep);
       }
*/
char *
mystrtok_r (char *str, const char *delims, char **nextp)
{
  if (str == NULL)
    str = *nextp;
  str += strspn (str, delims);
  *nextp = str + strcspn (str, delims);
  **nextp = 0;
  if (*str == 0)
    return NULL;
  ++*nextp;
  return str;
}
于 2013-07-03T05:35:03.770 回答
1

这取决于你将如何使用它。如果您想获取下一个令牌,然后是下一个(就像对字符串的迭代,那么您只需要将当前令牌复制到内存中。

long strtok2( char *strDest, const char *strSrc, const char cTok, long lOffset, long lMax)
{
  if(lMax > 0)
  {
    strSrc += lOffset;
    char * start = strDest;
    while(--lMax && *strSrc != cTok && (*strDest++ = * strSrc++) );
    *strDest = 0; //for when the token was found, not the null.
    return strDest - start - 1; //the length of the token
  }
  return 0;
}

我从http://vijayinterviewquestions.blogspot.com.au/2007/07/implement-strcpy-function.html获得了一个简单的 strcpy

const char* msg = "fred,jim,345,7665";
char * buffer[20];
long offset = 0;
while(length = strtok2(buffer, msg, ',', offset, 20))
{
  cout << buffer;
  offset += (length+1);
}
于 2013-07-03T03:10:26.497 回答
1

好吧,如果没有更多细节,就很难确切知道您想要什么。我猜你正在解析分隔的项目,其中连续的分隔符应该被视为零长度标记(这对于逗号分隔的元素通常是正确的)。我还假设一个空行算作一个零长度标记。这就是我的处理方式:

const char *token_begin = msg;
int length;

for(;;)
{
   length = 0;
   while(!isDelimiter(token_begin[length]))    //< must include \0 as delimiter
      ++length;

   //..do something here with token.  token is at: token_begin[0..length)

   if ( token_begin[length] != 0 )
      token_begin = &token_begin[length+1];    //skip beyond non-null delimiter
   else
      break;                                   //token null terminated.  exit
}
于 2013-07-03T04:48:21.827 回答
0

这是我的建议,我的代码是结构化的并使用全局变量pos(我知道全局变量是一种不好的做法,但只是为了给你一个想法),如果你需要 OOP,你可以用数据成员替换它。

int position, messageLength;
char token[MAX]; // MAX = Value greater than the maximum length
                 // of the tokens(e.g. 1,000);


bool hasNext()
{
  return position < messageLength;
}

char* next(const char* message)
{
  int i = 0;
  while (position < messageLength && message[position] != ',') {
    token[i++] = message[position];
    position++;
  }

  position++; // ',' found

  token[i] = '\0';
  return token;
}

int main(int argc, char **argv)
{
  const char* msg = "fred,jim,345,7665";
  position = 0;
  messageLength = strlen(msg);

  while (hasNext())
    cout << next(msg) << endl;

  return EXIT_SUCCESS;
}
于 2013-07-03T03:21:47.363 回答
0

如果您要将令牌存储在某个地方,那么在任何情况下都需要一个副本,并且strtok通过在其中使用字符串放置null终止字符来很好地做到这一点。

我看到避免复制它的唯一另一个选择是词法分析器,它读取字符串并通过状态机通过扫描字符串并将部分结果存储在缓冲区中来生成令牌,但在任何情况下,每个令牌都应至少存储在 null终止给你的字符串并没有真正保存任何东西。

于 2013-07-03T02:12:27.647 回答