使用字符串和数组
Working with Strings and Arrays
我正在创建一个程序来检查从出发机场到目的地机场的可能航空公司(这两个信息都将由用户输入)。我有一个包含所有航空公司编号及其相应机场的文件。例如,10A, LIM, LAX
包含航空公司编号 (10A)、出发机场 (LIM) 和目的地机场 (LAX)。每行包含以下格式的信息:航空公司编号、始发机场(缩写)和目的地机场(缩写)。
我会让 C 读取文件,它会使用 fgets
函数读取每一行,直到它到达与始发地机场和目的地机场都匹配的行。然后,它将 return 航空公司号码。如果没有,那么计算机将打印出没有航空公司有该航班。由于每一行都将被视为一个字符串,该字符串将存储在一个我称为 str[10000]
的字符数组中。我希望使用 strcat
函数存储航空公司编号。我想做的是 strcat(str[0], str[1])
这将添加,例如使用我与 10A 的示例是组合 10(将存储在 str[0] 中)和 A(将存储在 str [1]).由于某种原因,这种方法似乎不起作用。
我创建了函数 void route(char *origin, char *destination, char *airline[])
来打印航空公司号码,但我不知道如何使用数组并将它们存储到 *airline[]
中。我也初始化为 *airline[20]
因为可以有几家航空公司从用户指定的出发地到目的地。
我只是想确保我最终可以使用 strcmp
来比较用户输入的出发地机场和目的地机场的内容,如果匹配则报告航空公司号码。
请帮助我处理字符串和数组。当我尝试编译和 运行 程序时,在输入始发地和目的地机场后出现错误 Abort Trap 6
。我不知道为什么会这样。我将感谢任何人提供的任何帮助。提前致谢。
EDIT 这是我认为主要问题来自的函数...
void route(char *origin, char *destination, char *airline[])
{
char str[10000];
while (fgets(str, sizeof(str), fp) != NULL)
strcat(str[0], str[1]); // Combining the first element (10) with second element (A)
}
请注意,这段代码仍然不完整,但到目前为止我已经做到了这一点,因为我被卡住了。
EDIT 10A 不是航空公司号码的可能选择。它确实包含三个元素,而所有航空公司选择都只有两个元素,例如 3A。
EDIT 我添加了使用 fgets
函数的行:while (fgets(str, sizeof(str), fp) != NULL)
继续评论(因为看起来您需要帮助才能让 'logic' 灯泡闪烁),让我们看看您正在做什么,没有做什么,让您挑出来。首先让我们看看你的声明:
void route(char *origin, char *destination, char *airline[]);
void
对于函数类型来说是一个可怕的选择,您需要某种方法来衡量函数的 success/failure(例如 found/not-found 在您的情况下)。为 route
选择一个 type
这样你就可以 return 一个值来表示 success/failure在需要的时候。 void
有它的位置,例如就像在一个简单的函数中一样,它只打印输出,您在将函数参数 传递给函数之前 进行验证。否则,使用可以检查 return 值以确定函数是成功还是失败的类型。这里一个简单的 int
类型就可以了,returning 0
失败,1
(或任何非零数字,甚至负数)表示成功。
接下来,char *airline[]
是一个 类型为 char 的指针数组。这不是您要传递的字符串,例如 3A
,它是一个 类型 char 的数组(它将被转换为 指向 char*[ 的指针=142=] 当作为函数参数传递时)或 sting 文字,它只是一个指向 char 的 指针。所以你实际上想要 char *airline
而不是。
下一步,除非你将fp
声明为全局文件流指针(坏主意),否则你需要将FILE *fp
作为参数传递给route
。将这些信息放在一起,您对可用 route
的声明可能类似于:
int route (char *origin, char *dest, char *airline, FILE *fp);
根据您在问题中描述的内容,您需要使用 fgets
,但不清楚您实际可用于解析 airline
、origin
和 [= str
中的 34=] 由 fgets
填充。因此,让我们假设您没有其他可用于分隔字符串的函数,让我们使用始终可用的两种基本方法(数组索引和指针算术) 从 str
解析 airline
、origin
和 destination
。
这是基本方法,从str
开始,走一个指针(或使用数组索引),检查每个字符的值,并按要求分配字符(解析单词),直到您拥有所需的所有数据,或者已到达 str
的末尾(您必须防止出现任何一种情况)。
当处理一个字符串时,你 需要跟踪你的位置,并在合理的程度上预测接下来会发生什么,这样你就可以测试每个字符(对于你需要的,以及字符串的结尾),将它保存在需要保存的地方,或者继续将它保存在其他地方。在执行此操作时,您需要跟踪保存到每个位置的字符数(例如,分隔字符数组,如 arln
、org
和 dst
以进行比较您要查找的 airline
、origin
和 destination
)以确保您不会超出任何数组的末尾或分配的内存块,记住保存 +1 char
在每个数组或分配的块的末尾保存 '[=50=]'
(nul-byte 或 nul-terminating character)使每个字符串成为 有效字符串 的内存。 (了解 '[=50=]'
空字节的字符值只是 0
,因此您可以使用 '[=50=]'
或简单地 0
来终止字符串)
与其逐步解释可用的 route
函数的每一行可能是什么样子,不如让我提供一个带注释的示例,逐步帮助您理解逻辑(和层次在 C 中做几乎任何事情所需的思想和理解,您可以完全控制如何使用内存,并且您有责任确保正确使用它 -- 能力越大,责任越大).完成以下工作,如果您需要任何特定部分的帮助,请告诉我。
我故意在示例中同时使用了数组索引和指针算法,但对于这种类型的正常实现,我更喜欢出于注释中指出的原因的指针算法。以下所有内容都可以通过多种不同的方式完成,例如使用 fscanf
来简化一次读取和解析,或者使用 strtok
或 sscanf
来解析 str
填充通过 fgets
。这些函数只是帮助您完成您始终可以手动执行的操作,方法是将一个指针(或一对指针)沿着任何字符串向下移动。
我已经验证了在 route
中传递给 route
的参数作为示例,但您通常希望在调用函数(main
此处)中执行此操作,然后再进行致电 route
。因此,这是一个可用 route
的示例,它将单步执行包含以下格式的行的输入文件:
airline, origin, destination /* e.g. '3A, KOCH, KEGE' */
(commas
必须出现在每个标识符的末尾,因为它是当前编码的,但空格的数量无关紧要。' 3A, KOCH, KEGE'
也可以正常工作。你有完全控制这一点,只需更改每个字符的测试,您就可以完全消除 ','
要求,只需寻找 0-9, a-z, A-Z
来组成每个单词——这完全取决于您。)
route
将使用 fgets
读取数据文件中的每一行,将各个标识符解析为 arln
、org
和 dst
以与 airline
、origin
和 destination
传递给 route
、return 传递给 1
(如果找到匹配),或 0
错误或如果找不到匹配项。 route
的示例:
int route (char *origin, char *dest, char *airline, FILE *fp)
{
/* validate parameters - you can do this before calling route */
if (!origin || !*origin) {
fprintf (stderr, "error: origin 'NULL' or empty.\n");
return 0;
}
if (!dest || !*dest) {
fprintf (stderr, "error: dest 'NULL' or empty.\n");
return 0;
}
if (!airline || !*airline) {
fprintf (stderr, "error: airline 'NULL' or empty.\n");
return 0;
}
if (!fp) {
fprintf (stderr, "error: fp not open for reading.\n");
return 0;
}
char str[LINE] = "";
while (fgets (str, LINE, fp)) /* for each line in the input file */
{
char org[PARTS] = "", dst[PARTS] = "", arln[PARTS] = "",
*o = org, *d = dst, *a = arln, *s = str;
size_t n;
/* check for trailing '\n' included by 'fgets' indicating
* complete line read into str, then remove '\n' by overwriting
* with nul-terminating character.
*/
n = strlen (str);
if (str[n - 1] == '\n')
str[--n] = 0;
/* read airline flight from str into arln, will skip leading
* whitespace copying all other chars up to first ',' or '[=13=]'.
* (you can also use strtok to 'tokenize' the line)
*/
/* you can either use array indexing */
for (n = 0; n < PARTS - 1 && s[n] && s[n] != ','; n++) {
if (s[n] == ' ') continue; /* skip space */
a[n] = s[n];
}
a[n] = 0; /* nul-terminate arln (already done, but...) */
s += n; /* indexing wont advance 's', advance to char after 'arln'
* (to the ',') to start looking for beginning of 'org'.
*/
/* or use pointer arithmetic (cleaner as 's' is advanced)
*
* for (n = 0; n < PARTS - 1 && *s && *s != ','; n++, a++, s++) {
* if (*s != ' ') continue;
* *a = *s;
* }
* *a = 0;
*/
/* add checks to validate 'arln' length here if desired, and the same
* following filling 'org' and 'dst' if desired
*/
/* compare arln to airline */
if (strcmp (arln, airline) != 0)
continue; /* doesn't match, get next line */
/* find start of origin (org) in str (using pointer arithmetic)
* skipping forward until you find next '0-9', 'a-z' or 'A-Z'
* (you can use while (*s && isalnum (*s)) from ctype.h instead)
*/
while ( *s && ( (*s < '0' || '9' < *s) &&
(*s < 'a' || 'z' < *s) &&
(*s < 'A' || 'Z' < *s)))
s++;
if (!*s) { /* validate you are not at end of str */
fprintf (stderr, "error: no valid chars follow airline in str.\n");
return 0;
}
/* fill org ICAO from str */
for (n = 0; n < PARTS - 1 && *s && *s != ','; n++, o++, s++)
*o = *s;
*o = 0;
/* compare org to origin */
if (strcmp (org, origin) != 0)
continue; /* doesn't match, get next line */
/* find start of destination (dst) in str (using pointer arithmetic)
* skipping forward until you find next '0-9', 'a-z' or 'A-Z'
* (you can use while (*s && isalnum (*s)) from ctype.h instead)
*/
while ( *s && ( (*s < '0' || '9' < *s) &&
(*s < 'a' || 'z' < *s) &&
(*s < 'A' || 'Z' < *s)))
s++;
if (!*s) { /* validate you are not at end of str */
fprintf (stderr, "error: no valid chars follow origin in str.\n");
return 0;
}
/* fill dst ICAO from str */
for (n = 0; n < PARTS - 1 && *s && *s != ','; n++, d++, s++)
*d = *s;
*d = 0;
/* compare dst to destination */
if (strcmp (dst, dest) == 0)
goto found; /* match for airline, origin, destination found */
}
return 0; /* return 0 - indicating no route found */
found:
return 1; /* return 0 - indicating route found */
}
注意: route
本质上是将同一件事重复三次。从每个标识符的开头开始,将每个字符读入正确的数组,直到找到 ','
(或 *nul-byte),nul-terminate 你的新数组,然后移动到 str
中的下一个字符,跳过所有内容,直到找到 0-9, a-z, A-Z
(从下一个标识符开始),然后重复。
其使用的一个简短示例是:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { PARTS = 8, LINE = 128 }; /* constants - adjust as required */
/* specify a meaningful return type to guage success/failure */
int route (char *origin, char *dest, char *airline, FILE *fp);
int main (int argc, char **argv) {
char *ar = "3A", *or = "KOCH", *ds = "KEGE"; /* flight to find */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
if (route (or, ds, ar, fp))
printf ("airline '%s' origin '%s' destination '%s' -- found.\n",
ar, or, ds);
else
printf ("airline '%s' origin '%s' destination '%s' -- not found.\n",
ar, or, ds);
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
int route (char *origin, char *dest, char *airline, FILE *fp)
{
...
}
示例输入文件
$ cat dat/route.txt
1D, KEFD, KPRS
3A, KOCH, KEGE
8Z, KDT0, 3T2
示例Use/Output
$ ./bin/airline <dat/route.txt
airline '3A' origin 'KOCH' destination 'KEGE' -- found.
完成示例,确保您理解所有内容,如果您不理解,请告诉我(在您进行合理搜索以回答基本语法等问题后,请自行提问)
我正在创建一个程序来检查从出发机场到目的地机场的可能航空公司(这两个信息都将由用户输入)。我有一个包含所有航空公司编号及其相应机场的文件。例如,10A, LIM, LAX
包含航空公司编号 (10A)、出发机场 (LIM) 和目的地机场 (LAX)。每行包含以下格式的信息:航空公司编号、始发机场(缩写)和目的地机场(缩写)。
我会让 C 读取文件,它会使用 fgets
函数读取每一行,直到它到达与始发地机场和目的地机场都匹配的行。然后,它将 return 航空公司号码。如果没有,那么计算机将打印出没有航空公司有该航班。由于每一行都将被视为一个字符串,该字符串将存储在一个我称为 str[10000]
的字符数组中。我希望使用 strcat
函数存储航空公司编号。我想做的是 strcat(str[0], str[1])
这将添加,例如使用我与 10A 的示例是组合 10(将存储在 str[0] 中)和 A(将存储在 str [1]).由于某种原因,这种方法似乎不起作用。
我创建了函数 void route(char *origin, char *destination, char *airline[])
来打印航空公司号码,但我不知道如何使用数组并将它们存储到 *airline[]
中。我也初始化为 *airline[20]
因为可以有几家航空公司从用户指定的出发地到目的地。
我只是想确保我最终可以使用 strcmp
来比较用户输入的出发地机场和目的地机场的内容,如果匹配则报告航空公司号码。
请帮助我处理字符串和数组。当我尝试编译和 运行 程序时,在输入始发地和目的地机场后出现错误 Abort Trap 6
。我不知道为什么会这样。我将感谢任何人提供的任何帮助。提前致谢。
EDIT 这是我认为主要问题来自的函数...
void route(char *origin, char *destination, char *airline[])
{
char str[10000];
while (fgets(str, sizeof(str), fp) != NULL)
strcat(str[0], str[1]); // Combining the first element (10) with second element (A)
}
请注意,这段代码仍然不完整,但到目前为止我已经做到了这一点,因为我被卡住了。
EDIT 10A 不是航空公司号码的可能选择。它确实包含三个元素,而所有航空公司选择都只有两个元素,例如 3A。
EDIT 我添加了使用 fgets
函数的行:while (fgets(str, sizeof(str), fp) != NULL)
继续评论(因为看起来您需要帮助才能让 'logic' 灯泡闪烁),让我们看看您正在做什么,没有做什么,让您挑出来。首先让我们看看你的声明:
void route(char *origin, char *destination, char *airline[]);
void
对于函数类型来说是一个可怕的选择,您需要某种方法来衡量函数的 success/failure(例如 found/not-found 在您的情况下)。为 route
选择一个 type
这样你就可以 return 一个值来表示 success/failure在需要的时候。 void
有它的位置,例如就像在一个简单的函数中一样,它只打印输出,您在将函数参数 传递给函数之前 进行验证。否则,使用可以检查 return 值以确定函数是成功还是失败的类型。这里一个简单的 int
类型就可以了,returning 0
失败,1
(或任何非零数字,甚至负数)表示成功。
接下来,char *airline[]
是一个 类型为 char 的指针数组。这不是您要传递的字符串,例如 3A
,它是一个 类型 char 的数组(它将被转换为 指向 char*[ 的指针=142=] 当作为函数参数传递时)或 sting 文字,它只是一个指向 char 的 指针。所以你实际上想要 char *airline
而不是。
下一步,除非你将fp
声明为全局文件流指针(坏主意),否则你需要将FILE *fp
作为参数传递给route
。将这些信息放在一起,您对可用 route
的声明可能类似于:
int route (char *origin, char *dest, char *airline, FILE *fp);
根据您在问题中描述的内容,您需要使用 fgets
,但不清楚您实际可用于解析 airline
、origin
和 [= str
中的 34=] 由 fgets
填充。因此,让我们假设您没有其他可用于分隔字符串的函数,让我们使用始终可用的两种基本方法(数组索引和指针算术) 从 str
解析 airline
、origin
和 destination
。
这是基本方法,从str
开始,走一个指针(或使用数组索引),检查每个字符的值,并按要求分配字符(解析单词),直到您拥有所需的所有数据,或者已到达 str
的末尾(您必须防止出现任何一种情况)。
当处理一个字符串时,你 需要跟踪你的位置,并在合理的程度上预测接下来会发生什么,这样你就可以测试每个字符(对于你需要的,以及字符串的结尾),将它保存在需要保存的地方,或者继续将它保存在其他地方。在执行此操作时,您需要跟踪保存到每个位置的字符数(例如,分隔字符数组,如 arln
、org
和 dst
以进行比较您要查找的 airline
、origin
和 destination
)以确保您不会超出任何数组的末尾或分配的内存块,记住保存 +1 char
在每个数组或分配的块的末尾保存 '[=50=]'
(nul-byte 或 nul-terminating character)使每个字符串成为 有效字符串 的内存。 (了解 '[=50=]'
空字节的字符值只是 0
,因此您可以使用 '[=50=]'
或简单地 0
来终止字符串)
与其逐步解释可用的 route
函数的每一行可能是什么样子,不如让我提供一个带注释的示例,逐步帮助您理解逻辑(和层次在 C 中做几乎任何事情所需的思想和理解,您可以完全控制如何使用内存,并且您有责任确保正确使用它 -- 能力越大,责任越大).完成以下工作,如果您需要任何特定部分的帮助,请告诉我。
我故意在示例中同时使用了数组索引和指针算法,但对于这种类型的正常实现,我更喜欢出于注释中指出的原因的指针算法。以下所有内容都可以通过多种不同的方式完成,例如使用 fscanf
来简化一次读取和解析,或者使用 strtok
或 sscanf
来解析 str
填充通过 fgets
。这些函数只是帮助您完成您始终可以手动执行的操作,方法是将一个指针(或一对指针)沿着任何字符串向下移动。
我已经验证了在 route
中传递给 route
的参数作为示例,但您通常希望在调用函数(main
此处)中执行此操作,然后再进行致电 route
。因此,这是一个可用 route
的示例,它将单步执行包含以下格式的行的输入文件:
airline, origin, destination /* e.g. '3A, KOCH, KEGE' */
(commas
必须出现在每个标识符的末尾,因为它是当前编码的,但空格的数量无关紧要。' 3A, KOCH, KEGE'
也可以正常工作。你有完全控制这一点,只需更改每个字符的测试,您就可以完全消除 ','
要求,只需寻找 0-9, a-z, A-Z
来组成每个单词——这完全取决于您。)
route
将使用 fgets
读取数据文件中的每一行,将各个标识符解析为 arln
、org
和 dst
以与 airline
、origin
和 destination
传递给 route
、return 传递给 1
(如果找到匹配),或 0
错误或如果找不到匹配项。 route
的示例:
int route (char *origin, char *dest, char *airline, FILE *fp)
{
/* validate parameters - you can do this before calling route */
if (!origin || !*origin) {
fprintf (stderr, "error: origin 'NULL' or empty.\n");
return 0;
}
if (!dest || !*dest) {
fprintf (stderr, "error: dest 'NULL' or empty.\n");
return 0;
}
if (!airline || !*airline) {
fprintf (stderr, "error: airline 'NULL' or empty.\n");
return 0;
}
if (!fp) {
fprintf (stderr, "error: fp not open for reading.\n");
return 0;
}
char str[LINE] = "";
while (fgets (str, LINE, fp)) /* for each line in the input file */
{
char org[PARTS] = "", dst[PARTS] = "", arln[PARTS] = "",
*o = org, *d = dst, *a = arln, *s = str;
size_t n;
/* check for trailing '\n' included by 'fgets' indicating
* complete line read into str, then remove '\n' by overwriting
* with nul-terminating character.
*/
n = strlen (str);
if (str[n - 1] == '\n')
str[--n] = 0;
/* read airline flight from str into arln, will skip leading
* whitespace copying all other chars up to first ',' or '[=13=]'.
* (you can also use strtok to 'tokenize' the line)
*/
/* you can either use array indexing */
for (n = 0; n < PARTS - 1 && s[n] && s[n] != ','; n++) {
if (s[n] == ' ') continue; /* skip space */
a[n] = s[n];
}
a[n] = 0; /* nul-terminate arln (already done, but...) */
s += n; /* indexing wont advance 's', advance to char after 'arln'
* (to the ',') to start looking for beginning of 'org'.
*/
/* or use pointer arithmetic (cleaner as 's' is advanced)
*
* for (n = 0; n < PARTS - 1 && *s && *s != ','; n++, a++, s++) {
* if (*s != ' ') continue;
* *a = *s;
* }
* *a = 0;
*/
/* add checks to validate 'arln' length here if desired, and the same
* following filling 'org' and 'dst' if desired
*/
/* compare arln to airline */
if (strcmp (arln, airline) != 0)
continue; /* doesn't match, get next line */
/* find start of origin (org) in str (using pointer arithmetic)
* skipping forward until you find next '0-9', 'a-z' or 'A-Z'
* (you can use while (*s && isalnum (*s)) from ctype.h instead)
*/
while ( *s && ( (*s < '0' || '9' < *s) &&
(*s < 'a' || 'z' < *s) &&
(*s < 'A' || 'Z' < *s)))
s++;
if (!*s) { /* validate you are not at end of str */
fprintf (stderr, "error: no valid chars follow airline in str.\n");
return 0;
}
/* fill org ICAO from str */
for (n = 0; n < PARTS - 1 && *s && *s != ','; n++, o++, s++)
*o = *s;
*o = 0;
/* compare org to origin */
if (strcmp (org, origin) != 0)
continue; /* doesn't match, get next line */
/* find start of destination (dst) in str (using pointer arithmetic)
* skipping forward until you find next '0-9', 'a-z' or 'A-Z'
* (you can use while (*s && isalnum (*s)) from ctype.h instead)
*/
while ( *s && ( (*s < '0' || '9' < *s) &&
(*s < 'a' || 'z' < *s) &&
(*s < 'A' || 'Z' < *s)))
s++;
if (!*s) { /* validate you are not at end of str */
fprintf (stderr, "error: no valid chars follow origin in str.\n");
return 0;
}
/* fill dst ICAO from str */
for (n = 0; n < PARTS - 1 && *s && *s != ','; n++, d++, s++)
*d = *s;
*d = 0;
/* compare dst to destination */
if (strcmp (dst, dest) == 0)
goto found; /* match for airline, origin, destination found */
}
return 0; /* return 0 - indicating no route found */
found:
return 1; /* return 0 - indicating route found */
}
注意: route
本质上是将同一件事重复三次。从每个标识符的开头开始,将每个字符读入正确的数组,直到找到 ','
(或 *nul-byte),nul-terminate 你的新数组,然后移动到 str
中的下一个字符,跳过所有内容,直到找到 0-9, a-z, A-Z
(从下一个标识符开始),然后重复。
其使用的一个简短示例是:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { PARTS = 8, LINE = 128 }; /* constants - adjust as required */
/* specify a meaningful return type to guage success/failure */
int route (char *origin, char *dest, char *airline, FILE *fp);
int main (int argc, char **argv) {
char *ar = "3A", *or = "KOCH", *ds = "KEGE"; /* flight to find */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
if (route (or, ds, ar, fp))
printf ("airline '%s' origin '%s' destination '%s' -- found.\n",
ar, or, ds);
else
printf ("airline '%s' origin '%s' destination '%s' -- not found.\n",
ar, or, ds);
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
int route (char *origin, char *dest, char *airline, FILE *fp)
{
...
}
示例输入文件
$ cat dat/route.txt
1D, KEFD, KPRS
3A, KOCH, KEGE
8Z, KDT0, 3T2
示例Use/Output
$ ./bin/airline <dat/route.txt
airline '3A' origin 'KOCH' destination 'KEGE' -- found.
完成示例,确保您理解所有内容,如果您不理解,请告诉我(在您进行合理搜索以回答基本语法等问题后,请自行提问)