试图在一个字符之后复制字符串的其余部分

Trying to copy the remainder of a string after a character

我正在尝试复制用户输入字符串的剩余部分。它始终输入如下:airport、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();

}

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

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

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

char* find_digit(char* string)
{
  while(*string != '[=10=]') {
    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,您可以考虑通过将每个“,”替换为空终止符来创建子字符串 [=12=],然后根据子字符串的长度设置指向每个子字符串的指针上一个字符串。

或者,您可以通过字符串累积字符直到到达 , 然后终止缓冲区并开始将字符串的下一部分累积到下一个缓冲区中,并重复直到到达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 = '[=10=]';          // 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;

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

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 return,当字符不存在...

编辑:很想给出我自己的实现,让您了解指针在 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() returning NULL,因为它 发生。

虽然您可以使用指针和 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

例子Use/Output

$ ./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、Arrival time 和 Departure在一次调用中将时间放入单独的缓冲区。始终验证任何 scanf 函数的 RETURN 以确保发生预期的转化次数。

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

这里可以举一个简单的例子:

#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);
    }
}

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

相同的输入文件。

例子Use/Output

$ ./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、到达和离开时间的关联。

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