【问题标题】:Read file lines into one string seperated by \r\n将文件行读入一个由 \r\n 分隔的字符串
【发布时间】:2021-12-17 06:55:00
【问题描述】:

我需要读取一个看起来像这样的文件

foo
bar
foobar
barfoo

在 C 中并将其保存到类似于 "foo\r\nbar\r\nfoobar\r\nbarfoo\r\n"char buf[]

目前我正在尝试使用 fgets() 但我不确定如何将 \r\n 添加到我拥有的 char[] 中

void client(char* server_host) {
    int rc, client_socket;
    char buf[BUFFER_LEN];
    client_socket = connect_to_server(server_host, NON_RESERVED_PORT);
    printf("\nEnter a line of text to send to the server or EOF to exit\n");
    while(fgets(buf, BUFFER_LEN, stdin) != NULL) {
        send_msg(client_socket, buf, strlen(buf) + 1);
        rc = recv_msg(client_socket, buf);
        buf[rc] = '\0';
        printf("client received %d bytes  :%s: \n", rc, buf);
        printf("\nEnter a line of text to send to the server or EOF to exit\n");
    }
}

这是我尝试执行此操作的方式,但是当我使用 send_msg() 时,我需要使用我读入的每一行上的 \r\n 分隔符来分隔我正在使用的 buf。

【问题讨论】:

  • 你在socket中只得到\n吗?
  • 添加了我正在处理的部分
  • 您读取的文件是否包含\r\n?如果有,你是用二进制模式打开的吗?
  • 应该有一个代码段可以使用(用一对```s包围代码)。
  • fgets() 现在只是读取一行并在看到 \n 时停止。我想遍历整个文件并将其放入一个字符串中,\r\n 分隔行

标签: c io


【解决方案1】:

一个hacky,但最少的代码。

char buf[BUFFER_LEN];

// Read up to 2 less to be sure we have room for an added \r\n
while(fgets(buf, BUFFER_LEN - 2, stdin)) {

  // Use strcspn() to find offset of \n if it exists or the string length
  // Then write \r\n\ there
  strcpy(buf + strcspn(buf, "\n"), "\r\n");

  // send it
  send_msg(client_socket, buf, strlen(buf));
}

【讨论】:

  • 注意:如果行长于BUFFER_LEN - 2 字符,则会插入\r\n 中线。
  • @ikegami 没错。 OP 在排长队时很安静。我们可以一个接一个地使用fgetc(),但这会产生很多开销。
【解决方案2】:

听起来你是在说你有一个缓冲区buf,其中包含类似

{ 'a', 'b', 'c', '\n', 0 }
{ 'a', 'b', 'c', 0 }            // In the case of a line that's longer than BUF_SIZE-1.

但你想要一个包含

的缓冲区
{ 'a', 'b', 'c', '\r', '\n' }   // send_msg doesn't care about the NUL.
{ 'a', 'b', 'c' }

这只是一个检查最后一个字符是否是LF,并将LF和NUL替换为CR和LF的问题。

...
while (fgets(buf, BUFFER_LEN, stdin) != NULL) {
    size_t len = strlen(buf);
    if (buf[len-1] != '\n') {
       // The line didn't fit in the buffer.
       send_msg(client_socket, buf, len);
       continue;
    }

    buf[len-1] = '\r';
    buf[len] = '\n';
    ++len;
    send_msg(client_socket, buf, len);

    ...
}
...

此代码不能很好地处理用户输入的 NUL。就这个答案而言,这是GIGO 的情况。

【讨论】:

  • 谢谢,这就是我想要做的。我将尝试让它与我的代码一起使用。真的希望有一个简单的解决方案,但我想在 C 中这样做会更复杂。
  • 由于fgets 只读取到一个LF,它只能在末尾,这大大简化了事情。更新了答案。
  • 谢谢这个解决方案很有意义!
  • 黑客角:buf[len-1] 在读取的第一个字符是 空字符 时是 UB。
  • @chux - 恢复莫妮卡,哦,对,我考虑过,但认为领先的 NU 意味着一个空输入,这意味着 EOF,这意味着没有进入循环。当然,这是错误的。可以提供一个实际的 NUL。修复。
【解决方案3】:

这对于手头的任务来说可能完全是多余的,但我喜欢将数据封装在structs 中,并为这些structs 创建支持函数。所有样板文件都需要一些时间,但完成后通常会很好。

在这里,我将structtypedefined 变成了String。它有一个char* 表示实际数据,size 表示数据,capacity - 这是它当前能够保存的数据量。

typedef struct {
    char *data;
    size_t size;
    size_t capacity;
} String;

为了处理这样的String,我将从创建这些支持函数开始 - 希望具有自我解释的名称和签名:

String *String_create();
void String_destroy(String *s);
bool String_append(String *s, const char *rhs, size_t len);
bool String_getline(String *s, FILE *fp);

这是我们为此所需的最少函数集 - 但如果弹出任何其他内容,您当然可以添加更多 String 函数。

示例实现:

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

typedef struct {
    char *data;
    size_t size;
    size_t capacity;
} String;

String* String_create() {
    String *s = malloc(sizeof *s);         // allocate a String struct
    if(s) {
        s->size = 0;                       // initialize its values
        s->capacity = 10;
        s->data = malloc(s->capacity + 1); // some initial capacity + '\0'
        if(s->data == NULL) {
            free(s);                       // deal with allocation problems
            return NULL;
        }
        s->data[0] = '\0';                 // null terminate
    }
    return s;
}

void String_destroy(String *s) {           // deallocates a String
    free(s->data);
    free(s);
}

// Append to an existing string
bool String_append(String *s, const char *rhs, size_t len) {
    if(s->size + len > s->capacity) {        // check capacity
        // not enough space in the String - need to increase it

        size_t nc = (s->size + len) * 5 / 3; // make it grow some
        char* ns = realloc(s->data, nc + 1); // allocate more space
        if(ns == NULL) return false;         // could not increase capacity
        s->capacity = nc;                    // set new capacity
        s->data = ns;                        // and the new data pointer
    }
    // append rhs to our String
    memcpy(s->data + s->size, rhs, len);
    s->size += len;
    s->data[s->size] = '\0';                 // ... and null terminate
    return true;
}

// Convenience wrappers for the String_append function
bool String_append_char(String *s, char ch) {
    return String_append(s, &ch, 1);
}

bool String_append_String(String *s, const String *rhs) {
    return String_append(s, rhs->data, rhs->size);
}

// Read a line into a String from a stream
bool String_getline(String *s, FILE *fp) {
    s->size = 0;                             // zero out this String
    int ch;
    while((ch = fgetc(fp)) != EOF) {         // read until EOF
        // call the above append function
        if(String_append_char(s, ch) == false) return false;
        if(ch == '\n') return true;          // newline, return success
    }
    return s->size != 0;                     // at EOF, did we get some data?
}

使用该样板,可以像这样将文件中的所有内容读入String

int main() {
    String* str = String_create();      // The final String
    String* tmp = String_create();      // A temporary String to read lines

    while(String_getline(tmp, stdin)) { // Read lines until that fails
        // The String is at least one char long if String_getline succeeds so
        // this is safe:
        if(tmp->data[tmp->size-1] == '\n') { // got newline
            tmp->data[tmp->size-1] = '\r';   // replace it with \r
            String_append_char(tmp, '\n');   // and add \n
        }

        // Append this String to the final String
        if(String_append_String(str, tmp) == false) {
            printf("Could not append %s to %s\n", tmp->data, str->data);
            exit(1);
        }
    }
    String_destroy(tmp);               // not needed anymore

    send_msg(client_socket, str->data, str->size + 1);

    String_destroy(str);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-04
    • 2021-02-28
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    • 1970-01-01
    • 2019-03-01
    • 2011-06-30
    相关资源
    最近更新 更多