簡體   English   中英

如何使sscanf讀取到'\\ 0'字符

[英]How to make sscanf read until '\0' character

我希望name保留所有字符,直到'\\0'為止。

#include <stdio.h>

int main(){
    char line[] = "1999-08-01,14.547,0.191,United Kingdom";
    unsigned int year, month, day;
    float temp, uncertainty;
    char name[100];
    sscanf(line, "%u - %u - %u, %f , %f , %s", &year, &month,
                       &day, &temp, &uncertainty, name);
    printf("%u-%u-%u,%lf,%lf,%s\n", year, month, day, temp, uncertainty, name);
}

我可以像這樣做這項工作:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
    char line[] = "1999-08-01,14.547,0.191,United Kingdom";
    char* newline = malloc(strlen(line) + 2);
    strcpy(newline, line);
    newline[strlen(newline)] = '\n';
    newline[strlen(newline)] = '\0';
    unsigned int year, month, day;
    float temp, uncertainty;
    char name[100];
    sscanf(line, "%u - %u - %u, %f , %f , %[^\n]", &year, &month,
                       &day, &temp, &uncertainty, name);
    printf("%u-%u-%u,%lf,%lf,%s\n", year, month, day, temp, uncertainty, name);
}

但是我覺得這很不雅致。

sscanf並不是最優雅的界面,但是它具有很多功能。 其中之一是能夠找出您在輸入字符串中的位置,該功能使您可以提取(或僅指向)“輸入的其余部分”。

例如,之后;

int nchar = -1;
int nfield = sscanf(line, "%u - %u - %u, %f , %f , %n", &year, &month,
                    &day, &temp, &uncertainty, &nchar);

nchar將在名稱字段的line中包含偏移量(除非它仍為-1,表示sscanf無法與格式字符串匹配)。 如果那場延伸到結束line ,你可以接着直接(使用line + nchar )或復制到不同的字符串檢查,這是不是太長了。

如果line與其名稱相反,包含多行,並且您想要提取字符串直到換行符,則可以使用兩種%n格式,並且中間使用%*[^\\n] (星號會抑制復制,因此避免出現超限問題):

char name[NAME_MAX + 1];
int nstart = -1, nend = -1;
int nfield = sscanf(line, "%u - %u - %u, %f , %f , %n%*[^\n]%n", &year, &month,
                    &day, &temp, &uncertainty, &nstart, &nend);
if (nend > 0) {
  if (nend - nstart <= NAME_MAX) {
    memcpy(name, line + nstart, nend - nstart);
    name[nend - nstart] = 0;
  }
  else {
    /* name is too long */
  }
}
else if (nstart > 0) {
  /* Name was 0 bytes long. Sscanf requires that %[ match at least
   * one character; if not, it fails the scan.
   */
  name[0] = 0; /* Perhaps you wanted to signal an error
}
else {
  /* Line didn't match format */
}

顯然,我可以避免使用固定長度的緩沖區,而不必在知道大小的情況下通過動態分配緩沖區來檢查溢出:

char* name = NULL;
// ...
if (nend > 0) 
  name = strndup(line + nstart, nend - nstart);

// or, if you don't like strndup
//   name = malloc(nend - nstart + 1);
//   memcpy(name, line + nstart);
//   name[nend - nstart] = 0;

如果您真正想要的是動態分配的字符串,並且您具有兼容Posix的sscanf ,則可以使用m length修飾符來避免這種麻煩,這是最簡單的全方位解決方案。

char* name = NULL;
int nfield = sscanf(line, "%u - %u - %u, %f , %f , %m[^\n]", &year, &month,
                    &day, &temp, &uncertainty, &name);

有關詳細信息,請參見您的sscanf聯機幫助頁。 在動態分配name所有情況下,完成name操作后,請不要忘記釋放它。

這應該工作:

char line[] = "1999-08-01,14.547,0.191,United Kingdom";
unsigned int year, month, day;
float temp, uncertainty;
char name[100];
sscanf(line, "%u - %u - %u, %f , %f , %99[^\n]", &year, &month,
                   &day, &temp, &uncertainty, name);
printf("%u-%u-%u,%lf,%lf,%s\n", year, month, day, temp, uncertainty, name);

不會找到'\\n' ,但是由於不會達到99的限制,因此sscanf將繼續讀取直到字符串結束標記。

幾天前,我正在閱讀2003年版“ Unix系統編程:通信,並發,線程”一書的第2章,我研究了一個示例,該示例導致使用自定義分隔符將字符串分成令牌(它可以是或_或空格或其他任何字符)。 它使用了strtok()C庫函數。 這是經過調整以某種方式滿足您需求的示例。 我將提供2個文件:

makeargv.c

#include <errno.h>
#include <stdlib.h>
#include <string.h>

/* frees all the memory that was allocated by makeargv */
void freemakeargv(char **argv)
{
   if (argv == NULL)
      return;
   if (*argv != NULL)
      free(*argv);
   free(argv);
}
/* Now the function that breaks string s into tokens */ 
int makeargv(const char *s, const char *delimiters, char ***argvp) 
{
   int error;  int i;
   int numtokens;
   const char *snew;
   char *t;
   if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) 
    { errno = EINVAL; return -1; }
   *argvp = NULL; /* so that a failed call to malloc,will leave it NULL */
      /* now we consume any initial delimiters characters of input s */
   snew = s + strspn(s, delimiters);   /* snew is real start of string */
   if ((t = malloc(strlen(snew) + 1)) == NULL)
      return -1;
   strcpy(t, snew);
   numtokens = 0;
   if (strtok(t, delimiters) != NULL)  /* count number of tokens in s */
      for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ;
      /* next,create argument array for ptrs to the tokens */
   if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) 
    {
      error = errno;
      free(t);
      errno = error;
      return -1;
    }
      /* now insert pointers-to-tokens into the argument array */
   if (numtokens == 0)  free(t);
    else 
    {
      strcpy(t, snew);
      **argvp = strtok(t, delimiters);
      for (i = 1; i < numtokens; i++)
          *((*argvp) + i) = strtok(NULL, delimiters);
    }
    *((*argvp) + numtokens) = NULL;      /* append final NULL pointer */
    return numtokens;
}

main.c

#include <stdio.h>
#include <stdlib.h>
int makeargv(const char *s, const char *delimiters, char ***argvp);
void freemakeargv(char **argv);

int main()
{
    char delim[] = ",";
    int i, numtokens;
    char **myargv;  /* memory will be allocated dynamicaly, and has to bee freed before exit */

    char line[] = "1999-08-01,14.547,0.191,United Kingdom-UK";
    if ((numtokens = makeargv(line, delim, &myargv)) == -1) /* nakeargv() allocates memory for myargv, it may fail */
    {
      fprintf(stderr, "Failed to construct an argument array for %s\n", line);
      return 1;
    }
   printf(" The argument array contains:\n");
   for (i = 0; i < numtokens; i++)
      printf("%d:%s\n", i, myargv[i]);
   freemakeargv(myargv);    /* do not forget to free the memory! */
   return 0;
}

gcc -Wall -std = c99 -o標記程序main.c makeargv.c

並運行它

./令牌

@rici的好方法的變體:

如何使sscanf讀取到'\\ 0'字符

使用"%n"來應對“其余部分”。
"%n"記錄到該點為止的掃描偏移量(如果達到此距離)。
%*[^\\n]進行掃描,但不會按照OP的“保留在行中”將所有字符保存到一個'\\n'

使用它來分配結束字符串。

// Some untested code
typedef struct {
  unsigned int year, month, day;
  float temp, uncertainty;
  char *name;
} data_T;

// return 0 on success
int foo(data_T *dest, const char *line) {
  int start = 0;
  int end = 0;
  memset(dest, 0, sizeof *dest); // zero `dest`
  sscanf(line, "%u - %u - %u, %f , %f , %n%*[^\n]%n", 
    &dest->year, &dest->month, &dest->day, &dest->temp, &dest->uncertainty, 
     &start, &end);
  if (start == 0) {
    // line did nor scan properly, return error
    return 1;
  }
  if (end == 0) {
    end = start; // there was no non-white-space text after the `,`
  }
  size_t len = end - start;   
  dest->name = malloc(len + 1u);
  if (dest->name == NULL) {
    // Out of memory
    return 1;
  }
  memcpy(dest->name, line + start, len);
  dest->name[len] = '\0';

  printf("%u-%u-%u,%f,%f, %s\n", 
    dest->year, dest->month, dest->day, dest->temp, dest->uncertainty, dest->name);

  return 0;  // be sure to free dest->name when done with it.
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM