如何在C语言中按字符位置拆分字符串

3
我是使用C语言读取外部文本文件。输入内容不太好,可能看起来像这样;
0PAUL               22   ACACIA AVENUE                           02/07/1986RN666

正如您所看到的,我没有明显的分隔符,有时值之间没有空格。但是我知道每个值在拆分时应该有多长(以字符长度为单位)。具体如下:

id = 1
name = 20
house number = 5
street name = 40
date of birth = 10
reference = 5

我已经建立了一个结构来保存这些信息,并尝试使用fscanf读取文件。然而,我发现类似于“...”的东西并不能满足我的需求。

fscanf(file_in, "%1d, %20s", person.id[i], person.name[i]);

我实际使用的代码行试图获取所有输入,但您应该看到我的意思...

长期的目标是将输入文件重新格式化为另一个输出文件,这样会更容易阅读。

我知道我可能完全走错了方向,但如果有人能指点我正确的方向,我将不胜感激。如果您能对我明显的理解缺失宽容一些,我也会感激不尽。

谢谢您的阅读。

3个回答

2
使用fgets逐行读取,然后从输入行中提取每个字段。警告:缓冲区不执行范围检查,因此必须注意及时调整缓冲区大小。
例如像这样(我没有编译它,所以可能存在一些错误):
    void copy_substr(const char * pBuffer, int content_size, int start_idx, int end_idx, char * pOutBuffer)
    {
        end_idx = end_idx > content_size ? content_size : end_idx;
        int j = 0;
        for (int i = start_idx; i < end_idx; i++)
            pOutBuffer[j++] = pBuffer[i];
        pOutBuffer[j] = 0;
        return;
    }

    void test_solution()
    {
        char buffer_char[200];
        fgets(buffer_char,sizeof(buffer_char),stdin);   // use your own FILE handle instead of stdin
        int len = strlen(buffer_char);
        char temp_buffer[100];
        // Reading first field: str[0..1), so only the char 0 (len=1)
        int field_size = 1;
        int filed_start_ofs = 0;
        copy_substr(buffer_char, len, filed_start_ofs, filed_start_ofs + field_size, temp_buffer);

    }

2

scanf是一个很好的方法,你只需要使用缓冲区并多次调用sscanf并给出正确的偏移量即可。 例如:

char buffer[100];
fscanf(file_in, "%s",buffer);

sscanf(buffer, "%1d", person.id[i]);
sscanf(buffer+1, "%20s", person.name[i]);
sscanf(buffer+1+20, "%5d", person.street_number[i]);

等等,我觉得这是最简单的方法。

请考虑使用结构体数组而不是数组结构体,person.id[i]看起来很奇怪,换成person[i].id会更好。


1
如果您有固定的列宽,您可以使用指针算术来访问字符串str的子串。 如果您有一个起始索引begin
printf("%s", str + begin) ;

将打印从begin开始直到结尾的子字符串。如果您想要打印特定length的字符串,可以使用printf的精度限定符.*,该限定符需要一个最大长度作为附加参数:

printf("%.*s", length, str + begin) ;

如果你想将字符串复制到临时缓冲区,可以使用strncpy,如果缓冲区大于子字符串长度,它将生成一个空终止的字符串。你也可以根据上述模式使用snprintf:

char buf[length + 1];

snprintf(buf, sizeof(buf), "%.*s", length, str + begin) ;

这将提取前导和尾随空格,这可能不是您想要的。您可以编写一个函数来删除不需要的空格;在SO上应该有很多示例。
您还可以在复制子字符串时去除空格。下面的示例代码使用<ctype.h>中的isspace函数/宏来实现此操作:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

int extract(char *buf, const char *str, int len)
{
    const char *end = str + len;
    int tail = -1;
    int i = 0;

    // skip leading white space;
    while (str < end && *str && isspace(*str)) str++;

    // copy string
    while (str < end && *str) {
        if (!isspace(*str)) tail = i + 1;
        buf[i++] = *str++;
    }

    if (tail < 0) tail= i;
    buf[tail] = '\0';

    return tail;
}

int main()
{
    char str[][80] = {
        "0PAUL               22   ACACIA AVENUE                     02/07/1986RN666",
        "1BOB                1    POLK ST                           01/04/1988RN802",
        "2ALICE              99   WEST HIGHLAND CAUSEWAY            28/06/1982RN774"
    };
    int i;

    for (i = 0; i < 3; i++) {
        char *p = str[i];
        char id[2];
        char name[20];
        char number[6];
        char street[35];
        char bday[11];
        char ref[11];

        extract(id,     p + 0, 1);
        extract(name,   p + 1, 19);
        extract(number, p + 20, 5);
        extract(street, p + 25, 34);
        extract(bday,   p + 59, 10);
        extract(ref,    p + 69, 10);

        printf("<person id='%s'>\n", id);
        printf("    <name>%s</name>\n", name);
        printf("    <house>%s</house>\n", number);
        printf("    <street>%s</street>\n", street);
        printf("    <birthday>%s</birthday>\n", bday);
        printf("    <reference>%s</reference>\n", ref);
        printf("</person>\n\n");        
    }

    return 0;
}

然而,这里存在风险:当您在特定位置访问字符串str + pos时,应确保不超出实际字符串长度。例如,您的字符串可能在名称后终止。当您访问生日时,您会访问有效的内存,但它可能包含垃圾。
您可以通过使用空格填充完整字符串来避免此问题。

这真的很有用,谢谢。我已经采用了这种方法,并将其添加到我已有的代码中,从文件中提取行。我需要去分析一下你的提取函数。虽然我还有很多工作要做,但是这个方法让我克服了遇到的难题。 - chinrub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接