【问题标题】:Trying to copy the remainder of a string after a character尝试在字符后复制字符串的其余部分
【发布时间】:2019-08-20 22:28:44
【问题描述】:

我正在尝试复制用户输入字符串的其余部分。始终输入以下内容:机场、00:00、00:00。数字是飞行时间和停留时间。在我的代码中,我已经让它吐出机场,但尝试使用 strchr 查找第一个“,”之后的所有内容是一种痛苦。有没有办法可以删除机场并将其余部分放入新字符串并继续使用 strchr?我必须重新格式化用逗号分隔的字符串是目标。最终结果应该是“城市有飞行时间 tt.tt 小时和停留时间 tt.tt 小时”。我不能使用 scanf 等,所以看起来 strchr 和 strcopy 可能是我唯一的选择。任何帮助表示赞赏。

当我试图在字符串中的最后一个“,”之后查找字符时,我目前的错误提示。

分配规则:不要使用 strtok()、任何与 scanf 相关的函数、任何对字符串数据进行标记化的函数。

int main()
{
    int counter = 0;
    int flightTimeNum = 0;
    char userInput[81] ="";
    char userInputNew[81] = "";
    char cityNew[20] = "";
    char flightTime[20] = "";
    char layOverTime[20] = "";


    fgets(userInput, 81, stdin);
    char *city;
    char *flight;
    char *layover;

    city = strchr(userInput, ',');
    strncpy(cityNew, userInput, city - userInput);
    printf("%s\n" ,cityNew);

    layover = strrchr(userInput, ',');
    strncpy(layOverTime, userInput, layover - userInput);
    printf("%s\n", layOverTime);

    printf("Press ENTER key to Continue\n");
    getchar();

}

【问题讨论】:

  • 你看过strtok吗?
  • 对不起,我更新了我的帖子,规则是“不要使用 strtok(),任何与 scanf 相关的函数,任何对字符串数据进行标记化的函数”
  • 当 strchr 似乎只喜欢特定角色的一个实例时,我基本上一直在努力弄清楚该怎么做,所以任何帮助都可能挽救我的理智!跨度>
  • 由于这是一项作业,我不会发布答案,但这里有一个提示。 strncpy(flightTime, userInput, flightTime - userInputNew); --> 这看起来根本不对。 flightTimeuserInputNew 不指向同一个数组。那个减法不会给你你想要的。您可能希望将您的算术基于cityflight。但是你也应该检查你的假设 city > flight 并且两者都不是NULL。
  • 你还想确保你为空终止你的字符串

标签: c string c-strings strcpy strchr


【解决方案1】:

如果你想使用strchr,你可以考虑通过用空终止符\0替换每个','来创建子字符串,然后根据前一个字符串的长度设置指向每个子字符串的指针。

或者,您可以通过字符串累积字符直到到达,,然后终止缓冲区并开始将字符串的下一部分累积到下一个缓冲区中,然后重复直到到达userInput

第一种方法的示例,请注意您应该验证您是否能够创建预期数量的子字符串等,以防用户输入无效数据。

// NULL terminate each substring
char *p = userInput;    // Point to the beginning of the string
for(;;) {
    p = strchr(p, ','); // Find first ',' starting from 'p'
    if (!p) break;      // If not found break out of loop
    *p = '\0';          // otherwise replace ',' with a null terminator
    ++p;                // move pointer 'p' to the next character
}

// Setup pointers to each substring
city = userInput; 
flight = city + strlen(city) + 1;
layover = flight + strlen(flight) + 1;

【讨论】:

  • 用 nuls 替换字符是我通常做的事情(以及 strtok() 在内部做的事情),因为它允许您使用 strlen() 和其他人。但请注意,当内存真正只读(例如存储在 ROM 中)时,它会在 const char* 上失败。
  • @Matthieu,很好,但是该示例确实表明这是作为用户输入出现的,因此不适用于这种特殊情况。
  • 是的,这通常不是问题(即使const char* 也可以转换为char*...)。我只是想提出这一点,因为它发生在我的 sxmlc 解析器中:人们在发生此类事情的嵌入式操作系统中使用它(配置文件存储在 PROM 中)并且写入它会导致核心转储......
  • *p++ = 0; 抱歉 ;)
  • @Matthieu,无需抱歉,我们始终感谢您的帮助(至少应该如此)。由于 OP 显然在学习,无论对错,我选择将每个步骤分开,以便我可以对其进行评论,并希望能弄清楚发生了什么。
【解决方案2】:

虽然您可以使用指针和strchr(或简单地将指针沿缓冲区向下移动)解析每一行的信息,但在处理格式化输入时,使用 sscanf这样的格式化输入函数会有很大帮助。

让我们来看看两者。在您的问题中,您想用fgets 阅读(好),然后找到第一个逗号,然后将该行的其余部分复制到新缓冲区。到目前为止一切都很好。唯一需要注意的是,一旦找到第一个逗号,您仍然必须将指针从逗号前移,跳过任何空格,直到在其余时间(该行的其余部分)到达第一个字符。

一种简单的方法是:在第一个逗号之后输出字符串的其余部分,省略任何中间空格:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXC 1024   /* if you need a constant, define one (or more) */

int main (void) {

    char buf[MAXC];

    while (fgets (buf, MAXC, stdin)) {      /* read with fgets */
        char *p;
        buf[strcspn (buf, "\r\n")] = 0;     /* trim '\n' from end */
        if ((p = strchr (buf, ','))) {      /* find 1st ',' */
            do
                p++;                    /* advance pointer */
            while (*p && isspace (*p)); /* to end or 1st non-whitespace */
        }
        printf ("rest of string: '%s'\n", p);   /* output rest of line */
    }
}

注意:在声明常量时,不要跳过缓冲区大小。)

输入文件示例

$ cat dat/airport_times.txt
KOCH, 01:01, 01:51
KAXX, 03:50, 05:40
KADS, 07:40, 09:30

使用/输出示例

$ ./bin/airport_gettimes <dat/airport_times.txt
rest of string: '01:01, 01:51'
rest of string: '03:50, 05:40'
rest of string: '07:40, 09:30'

现在让我们看看使用fgets 进行读取和格式化输入函数sscanf 来解析每一行中所有需要的值。 (或者您可以使用fscanf 在一次调用中完成,但您最好使用fgets 然后sscanf 来(1)确保您使用一整行数据并且(2)可以独立验证读取并解析值。

方法类似,使用fgets 读取,但不是使用strchr 查找第一个逗号,而是使用sscanf 在一次调用中将ICAO、到达时间和离开时间读取到单独的缓冲区中。始终验证任何scanf 函数的返回值,以确保发生预期的转化次数。

提前考虑,我们不要只使用三个独立且不相关的缓冲区。相反,让我们声明一个struct 来保存所有三个icao, ariv &amp; dept 缓冲区。这样,如果将多个条目读入内存,您可以简单地声明一个结构数组并为以后的用户读取/解析所有值。 (数组留给你)

这里有一个简单的例子:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXC 1024   /* if you need a constant, define one (or more) */
#define ICAO    6   /* will work for ICAO and time buffer lengths */

typedef struct {
    char icao[ICAO],
        ariv[ICAO],
        dept[ICAO];
} flight_t;

int main (void) {

    char buf[MAXC];

    while (fgets (buf, MAXC, stdin)) {      /* read with fgets */
        flight_t flt = { .icao = "" };      /* declare struct */
        /* separate all value in line into struct, validating return */
        if (sscanf (buf, "%5[^,], %5[^,], %5[^,]", 
                    flt.icao, flt.ariv, flt.dept) == 3)
            /* if sscanf succeeds, output (or use) the values */
            printf ("%s  %s (arrival)  %s (departure)\n",
                    flt.icao, flt.ariv, flt.dept);
    }
}

(注意:如何在每个字符类转换说明符之前使用field-width修饰符来保护每个缓冲区的数组边界"%5[^,], %5[^,], %5[^,]"。如果您对格式字符串有任何疑问,请直接询问。)

相同的输入文件。

使用/输出示例

$ ./bin/airport_parsetimes <dat/airport_times.txt
KOCH  01:01 (arrival)  01:51 (departure)
KAXX  03:50 (arrival)  05:40 (departure)
KADS  07:40 (arrival)  09:30 (departure)

这里的好处是,您现在将所有值协调为单个对象,但独立存储,因此您可以根据需要访问任何 icao, arivdept 值,而对于结构数组,仍然能够在任何结构成员上对数组进行排序,同时保持 icao、到达和离开时间的关联。

有很多很多的方法可以将拼图的各个部分拼凑在一起。下一步是根据需要动态分配 struct 和 realloc 数组,以允许您读取未知且无限数量的记录(最多可达计算机的物理内存)。然而,这两种方法,(1) 使用指针解析缓冲区,或 (2) 使用格式化输入函数涵盖了大多数情况。剩下的就是根据需要建立在该基础上。

【讨论】:

    【解决方案3】:

    strchr() 将返回一个指向字符的指针,这意味着它将在字符串中向前移动。您可以将其用作新字符串以进行进一步的操作:

    char str[] = "airport, 00:00, 00:00.";
    char* rem = strchr(str, ','); // rem is now ", 00:00, 00:00."
    rem += 2; // rem is now "00:00, 00:00."
    rem = strchr(rem, ','); // rem is now ", 00:00, 00:00."
    rem += 2; // rem is now "00:00."
    *strchr(rem, '.') = 0; // rem is now "00:00" (dot removed)
    

    当然,当字符不存在时,您应该检查NULL 返回...

    编辑:我很想给出我自己的实现,让你看看指针在 C 中是如何工作的:

    char str[] = "airport, 00:00, 00:00.";
    char* city = str; // "airport, 00:00, 00:00."
    char* departure = strchr(str, ','); // ", 00:00, 00:00."
    char* arrival = strchr(departure+1, ','); // ", 00:00."
    // Clean up
    *departure = 0; // city is now "airport"
    departure += 2; // depature is now "00:00, 00:00."
    *arrival = 0; // departure is now "00:00"
    arrival += 2; // arrival is now "00:00."
    *strchr(arrival, '.') = 0;  // arrival is now "00:00"
    

    同样,如果是用户输入,您必须检查strchr()是否返回NULL,因为它发生。

    【讨论】:

      【解决方案4】:

      这是使用函数查找第一个数字的替代解决方案。

      如果你只知道你关心的字符串以数字开头,这将比 strstr() 工作得更好。

      #include <stdio.h>
      #include <string.h>
      
      char* find_digit(char* string)
      {
        while(*string != '\0') {
          char ch = *string;
      
          if(*string >='0' && *string <= '9') {
            return string;
          }
          string++;
      
        }
      }
      int main()
      {
        char str[] = "airport, 00:00, 00:00.";
      
        char* ret = find_digit(str);
      
        char* remainder = ret + strlen(needle);
      
        printf("Remainder is: %s\n",remainder);
      
        return 0;
      }
      

      【讨论】:

      • 在这种情况下使用strchr() 更容易。机场名称可能未知。
      猜你喜欢
      • 2015-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多