如何使用空格和换行符遍历文本文件,对每个字符串执行函数,然后以原始格式再次打印出来?

How to iterate through text file with whitespace and newlines, perform a function upon each string, and print out again in the original format?

提问人:user20125552 提问时间:9/28/2023 最后编辑:Jonathan Leffleruser20125552 更新时间:9/28/2023 访问量:50

问:

我需要浏览类似格式的文本文件,如下所示:

fire dwarf stone
rock beer arrow lion
sword shield
archstone
dragon gold
silver
hall
goblet chest axe

Axe 后面有一个换行符(“axe\n”)。我需要将每个单词传递到一个函数中,然后在相同的位置、空格和换行符完全相同的位置打印回每个修改后的单词。

我遇到了成为 C 新手的问题。我发现了一些复杂的字符串格式选项,允许我从这里取一整行,包括空格,但我无法从行中提取单个单词。如果我只浏览整个文件,我无法弄清楚如何检查换行符,也无法弄清楚如果我这样做将如何保存位置。fscan("%s", word)

我尝试遍历字符串,使用字符作为跟踪器,将每个字符复制到一个新字符串中,每当跟踪器字符遇到“”时,我都会返回该字符串,但后来我不知道如何查看是否有换行符,因为使用两个字符跟踪器来检查“\”和“n”不起作用。

我目前的尝试是

while (fscanf(filename, "%s", line) == 1) {
                if (...) {
                    printf("%s ", line);
                } else {
                    printf("%se ", line);
                }
                if (getchar() == '\n') printf("\n");
            }

但这也行不通。我可能只是在搜索错误的问题,但我根本找不到任何方法来查看是否有换行符,同时根据需要单独解析单词。

C 字符串 IO 换行符

评论

2赞 Jonathan Leffler 9/28/2023
由于函数系列对空间非常粗心,因此您不太可能使用它们。我希望阅读行(或 POSIX)并使用和隔离单词和空格。您的示例仅显示单词之间的单个空格,但如果您可以保留多个空格,则可以轻松工作。如果您不必担心多个空格,则在格式中使用字符串将读取一个单词和一个空格(空白或换行符)。由于您规定后面有一个换行符,那可能会起作用。fscanf()fgets()getline()strspn()strcspn()%s%caxe
1赞 Fe2O3 9/28/2023
fscanf(filename, ...让我怀疑你是否真的有一个打开的文件。对于指针来说,这不是一个好名字。FILE
1赞 Jonathan Leffler 9/28/2023
您有 — 使用文件流 () 的名称具有误导性。常规名称是 ,或该名称的某种变体(、、、...)。while (fscanf(filename, "%s", line) == 1) {filenameFILE *FILE *fp;ifpfpinfin
0赞 Jonathan Leffler 9/28/2023
您可能会侥幸逃脱格式,然后替换为我即将假设您重命名为文件流。循环读取空格并回显它们,在 EOF 或获得非空格时停止,并将非空格放回输入流中,供下一个读取。您将不得不担心文件中的前导空格 - 会愉快地跳过它。%sif (getchar() == '\n') printf("\n");int c; while ((c = getc(fp)) != EOF && isspace(c)) putchar(c); if (c != EOF) ungetc(c, fp);filenamefpfscanf()%s
0赞 user20125552 9/28/2023
不知道 *fp 约定,或者更确切地说是忘记了这里。我概括了我的实际文件名,这样它更易读,或者我是这么想的。尝试使用这个东西,它确实可以用于此目的,所以谢谢!坦率地说,我有点不确定它为什么有效,但我想我可以稍后再查一下。你介意解释一下这件事吗?我看了一下语法,老实说,我不确定我会用它做什么。int c...fgets()

答:

1赞 Fe2O3 9/28/2023 #1

考虑将其编写为过滤器,处理 和 .这极大地简化了代码!以下内容将满足您的需求,并迅速而沉着!stdinstdout

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

char *procWrd( char *word ) {
    static char obuf[ 64 ];
    sprintf( obuf, "%s-%.2s", word, word );
    return obuf;
}

int main( void ) {
    int ch;
    char word[ 64 ]; // big enough!
    int wrdChr = 0;

    while( ( ch = getchar() ) != EOF )
        if( isspace( (unsigned char)ch ) ) { // guaranteed LF at EOF
            if( wrdChr ) {
                word[ wrdChr ] = '\0'; // terminate string
                printf( "%s", procWrd( word ) ); // output modified version
                wrdChr = 0; // reset
            }
            putchar( ch ); // output whitespace
        } else
            word[ wrdChr++ ] = ch; // buffer characters of word

    return 0;
}

调整每个单词的版本如下所示:

fire-fi dwarf-dw stone-st
rock-ro beer-be arrow-ar lion-li
sword-sw shield-sh
archstone-ar
dragon-dr gold-go
silver-si
hall-ha
goblet-go chest-ch axe-ax

运行文件两次以获得真正的乐趣:
猫文件.txt |项目 |prog > outfile.txt

0赞 Jonathan Leffler 9/28/2023 #2

使用 和 :fgets()strspn()strcspn()

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

static const char whisp[] = " \t\n";

static void mapper(size_t n_bytes, char *data)
{
    for (size_t i = 0; i < n_bytes; i++)
    {
        unsigned char c = data[i];
        if (isupper(c))
            data[i] = tolower(c);
        else if (islower(c))
            data[i] = toupper(c);
        /* else leave alone */
    }
}

int main(void)
{
    char line[2048];
    while (fgets(line, sizeof(line), stdin))
    {
        char *data = line;
        char *eol = line + strlen(line);
        size_t n_bytes;
        while (data < eol)
        {
            if ((n_bytes = strspn(data, whisp)) > 0)
            {
                fwrite(data, n_bytes, 1, stdout);
                data += n_bytes;
            }
            if ((n_bytes = strcspn(data, whisp)) > 0)
            {
                mapper(n_bytes, data);
                fwrite(data, n_bytes, 1, stdout);
                data += n_bytes;
            }
        }
    }
    return 0;
}

使用“读取空格”循环的代码:

/* SO 7719-2181 */
#include <ctype.h>
#include <stdio.h>
#include <string.h>

static void mapper(size_t n_bytes, char *data)
{
    for (size_t i = 0; i < n_bytes; i++)
    {
        unsigned char c = data[i];
        if (isupper(c))
            data[i] = tolower(c);
        else if (islower(c))
            data[i] = toupper(c);
        /* else leave alone */
    }
}

int main(void)
{
    char line[1024];
    while (scanf("%1023s", line) == 1)
    {
        mapper(strlen(line), line);
        fputs(line, stdout);
        int c;
        while ((c = getchar()) != EOF && isspace(c))
            putchar(c);
        if (c != EOF)
            ungetc(c, stdin);
    }
    return 0;
}

两者都对标准输入进行大小写转换,将其写入标准输出。例如,在其源代码上运行第二个程序会产生:

/* so 7719-2181 */
#INCLUDE <CTYPE.H>
#INCLUDE <STDIO.H>
#INCLUDE <STRING.H>

STATIC VOID MAPPER(SIZE_T N_BYTES, CHAR *DATA)
{
    FOR (SIZE_T I = 0; I < N_BYTES; I++)
    {
        UNSIGNED CHAR C = DATA[I];
        IF (ISUPPER(C))
            DATA[I] = TOLOWER(C);
        ELSE IF (ISLOWER(C))
            DATA[I] = TOUPPER(C);
        /* ELSE LEAVE ALONE */
    }
}

INT MAIN(VOID)
{
    CHAR LINE[1024];
    WHILE (SCANF("%1023S", LINE) == 1)
    {
        MAPPER(STRLEN(LINE), LINE);
        FPUTS(LINE, STDOUT);
        INT C;
        WHILE ((C = GETCHAR()) != eof && ISSPACE(C))
            PUTCHAR(C);
        IF (C != eof)
            UNGETC(C, STDIN);
    }
    RETURN 0;
}

(两次出现 被转换为 ,并转换为 。代码的其余部分为大写,但标点符号保持不变。EOFeofSOso

代码不会对输出操作进行错误检查——严格来说,它应该这样做。