【问题标题】:Reverses lines from a file given parameter从给定参数的文件中反转行
【发布时间】:2016-03-28 03:43:09
【问题描述】:

我必须编写一个函数 void reverse_file(FILE* file),它每次从参数文件中读取一行到一个本地字符数组缓冲区,该缓冲区的大小#defined 为预处理器常量 MAXLINE。对于每一行,该函数调用前面的 reverse_string 函数来反转该行,然后将反转的行写入标准输出。

我有 reverse_string 方法

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

void reverse_String(char* str);
int main()
{
    char str [256];
    strcpy_s(str, "Hello");
    reverse_String(str);
    return 0;
}
void reverse_String(char* str)
{
    int i, j;
    char temp;
    i=j=temp=0;

    j=strlen(str)-1;
    for (i=0; i<j; i++, j--)
    {
        temp=str[i];
        str[i]=str[j];
        str[j]=temp;
    }
    printf("%s",str);
}

我只需要关于 reverse_file(FILE* file) 部分的帮助。

感谢任何帮助。

【问题讨论】:

  • 假设你有一个三行文件:abcdefghi。你需要输出cbafedihg,还是ghidefabc,或者可能是ihgfedcba?无论必需的输出是什么,您都需要查找标准 C fgets() 或 POSIX getline() 以读取输入行。然后在适当的时候调用你的反向字符串函数。请注意,通常最好将输出与反转分开。
  • 另外,printf(str) 很糟糕——非常糟糕。您应该使用printf("%s\n", str)puts(str)。危险在于您正在打印的字符串将包含% 序列,并且您没有为printf() 提供参数来处理这些%s 序列。
  • @JonathanLeffler 第二个。 abc 变成 cba
  • 您似乎缺少您的#define。您看起来打算将每行中允许的 最大字符数 作为定义。你会想要#define MAXC 256 之类的东西(紧跟在#include ... 语句下,然后char str [MAXC] = ""; 使用它。(注意:你想使用defines 而不是幻数(例如256) 分散在您的代码中。您也可以使用 enum 来定义相同的常量而不是 #define,例如 enum { MAXC = 256 };
  • @DavidC.Rankin 完全忘记了这一点,谢谢伙计

标签: c string function file


【解决方案1】:

也许是这样的?

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

void reverse_String(char* str)
{
    int i, j;
    char temp;
    i=j=temp=0;

    j=strlen(str)-1;
    for (i=0; i<j; i++, j--) {
        temp=str[i];
        str[i]=str[j];
        str[j]=temp;
    }
    printf("%s",str);
 }

 void reverse_File(FILE* file) {
     char* line;
     size_t len = 0;
     ssize_t read;

     while ((read = getline(&line, &len, file)) != -1) {
        reverse_String(line);
     }
 }

int main(void) {

    FILE * fp;

    fp = fopen("Maze.txt", "r");

    if (fp == NULL)
        exit(EXIT_FAILURE);

    reverse_File(fp);
}

【讨论】:

  • 除非你没有从getline 读取的每个line 中修剪'\n',这意味着每个反向行将以换行符开头。使用面向行的输入时,应始终删除尾随'\n''\r\n'(DOS/windows)。
  • 相当整洁。几个小问题。 (1) 您应该确保line 可以安全地传递给free()(根据getline()应用程序应确保*lineptr 是可以传递给free() 函数的有效参数.),所以将line 设置为 0 是个好主意。 (2) 你应该释放由getline() 分配的内存,即使文件是空的,所以第一次读取操作返回-1(它不返回EOF,很有趣)。而且你还需要处理行尾。
  • @DavidC.Rankin:对于 WIndows 上的文本文件,I/O 子系统应在输入时将尾随 CRLF 转换为仅 NL(又名 LF),并在输出时将 NL 替换为 CRLF。如果文件来自 Windows 到 Unix,或者如果文件是为二进制 I/O 打开的,那么如果它真的是一个文本文件,你需要担心行尾。
  • 好极了——我今天学到了一些关于那个神秘操作系统的新东西。谢谢,所以在 I/O 期间,Windows 将摆脱 '\r' 的普通文件,而在 Linux 上读取时,只有那些被复制的 windows->linux 才会有 '\r'。这就解释了没有与无关的回车相关的问题:)
【解决方案2】:

您已经有了一个很好的答案,但还有一些微妙的问题需要考虑。如果您使用面向行 输入法(fgetsgetline),则应删除'\n''\r\n'(DOS/windows)行结尾读取并包含在fgetsgetline 的原始行。 (这就是为什么您在原始文件的输出中有一个初始空行)。

删除行尾的一种简单方法是简单地用 nul-terminating 字符覆盖遇到的任一行尾中的第一个。为此,您的主程序逻辑如下所示:

while (fgets (str, MAXC, fp)) {     /* read each line */
    rmcrlf (str);                   /* trim \r or \n  */
    strrev (str);                   /* reverse string */
    printf ("%s\n", str);           /* output reverse */
}

您的rmcrlf(删除回车换行)可能如下所示:

/** stip trailing newlines and carraige returns by overwriting with
 *  null-terminating char. 's' is modified in place.
 */
void rmcrlf (char *s)
{
    if (!s || !*s) return;          /* validate not NUL and not empty */
    char *p = strpbrk (s, "\r\n");  /* locate first line-ending char  */
    *p = 0;                         /* set to nul-terminating char    */
}

(注意:您可以使用strlen(或指针和循环)来查找字符串的结尾并向后工作,但strpbrk 允许您在前向搜索'\n''\r'方向而不需要找到终点并向后工作)

然后您可以以相同的方式反转您的字符串(但使用strrchr 来查找结尾而不是strlen

/** strrev - reverse string 's' in place.
 *  The original string is not preserved.
 *  If 's' is not valid, no action taken.
 */
void strrev (char *s)
{
    if (!s || !*s) { /* validate s not NUL and not empty */
        printf ("strrev() error: invalid string\n");
        return;
    }

    char tmp;
    char *begin = s, *end = strrchr (s, 0) - 1;

    while (end > begin) {
        tmp = *end;
        *end-- = *begin;
        *begin++ = tmp;
    }
}

您可以按如下方式将所有部分放在一起。这只是处理相同任务的另一种方法。注意:以下代码使用enum 而不是#define 来设置定义常量(只是为了显示替代方法)。另请注意:代码将从作为第一个参数给出的文件中读取(或默认情况下从stdin,如果没有给出文件名):

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

enum { MAXC = 256 };

void strrev (char *s);
void rmcrlf (char *s);

int main (int argc, char **argv) {

    char str[MAXC] = "";
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file is open */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (str, MAXC, fp)) {     /* read each line */
        rmcrlf (str);                   /* trim \r or \n  */
        strrev (str);                   /* reverse string */
        printf ("%s\n", str);           /* output reverse */
    }

    if (fp != stdin)         /* close file (if not stdin) */
        fclose (fp);

    return 0;
}

/** strrev - reverse string 's' in place.
 *  The original string is not preserved.
 *  If 's' is not valid, no action taken.
 */
void strrev (char *s)
{
    if (!s || !*s) { /* validate s not NUL and not empty */
        printf ("strrev() error: invalid string\n");
        return;
    }

    char tmp;
    char *begin = s, *end = strrchr (s, 0) - 1;

    while (end > begin) {
        tmp = *end;
        *end-- = *begin;
        *begin++ = tmp;
    }
}

/** stip trailing newlines and carraige returns by overwriting with
 *  null-terminating char. 's' is modified in place.
 */
void rmcrlf (char *s)
{
    if (!s || !*s) return;
    char *p = strpbrk (s, "\r\n");
    *p = 0;
}

示例输入

$ cat dat/strtorev.txt
abc def ghi jkl mno pqr
ABC DEF GHI JKL MNO PQR

输出

$ ./bin/strrev_line <dat/strtorev.txt
rqp onm lkj ihg fed cba
RQP ONM LKJ IHG FED CBA

(注意:正如 Johnathan 在评论中所问的那样,您可以如上所示反转该行,或者您可以反转每个单词(例如cba fed ihg...)确保您已正确说明您需要做什么,如果不是我们可以提供更多帮助)。

如果您有任何问题,请告诉我。

【讨论】:

  • 当然,很高兴为您提供帮助。只是清理一些松散的末端。祝你编码顺利。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
  • 2014-12-30
  • 2018-09-11
  • 1970-01-01
  • 2017-07-06
相关资源
最近更新 更多