【问题标题】:Reading strings from stdin从标准输入读取字符串
【发布时间】:2014-05-30 21:36:30
【问题描述】:

我正在尝试使用这种格式从标准输入读取一行:

Boston "New York" "San Francisco" Memphis(请注意,括号中包含空格的字符串。另请注意,每个城市名称都用空格分隔。) 我尝试一次使用 scanf 阅读此内容, fgets 整行,然后进行标记,但结果不佳。 我假装是将所有内容存储在多维 char 数组中以备后用。

关于我应该如何解决这个问题的任何建议?提前谢谢!

【问题讨论】:

  • 足够简单,可以阅读整行并自己解析。如果您遇到的第一个非空白字符不是",则阅读直到下一个空格。如果是,请阅读下一个",假设您不需要担心转义引号。

标签: c scanf fgets


【解决方案1】:

您可以阅读整行内容并自己轻松解析。如果您遇到的第一个非空白字符不是“,则阅读直到下一个空格。如果是,则阅读直到下一个“,假设您不需要担心转义引号。

这是一个简单的实现:

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

#define MAX_BUFFER 100
#define MAX_STRINGS 10

int main(void) {
    char buffer[MAX_BUFFER];

    if ( !fgets(buffer, MAX_BUFFER, stdin) ) {
        fprintf(stderr, "Couldn't get input.\n");
        return EXIT_FAILURE;
    }
    else {

        /*  Remove trailing newline, if present  */

        size_t length = strlen(buffer);
        if ( length && buffer[length - 1] == '\n' ) {
            buffer[length - 1] = '\0';
        }
    }

    char *my_strings[MAX_STRINGS + 1] = {NULL};
    int read_strings = 0;
    char *buf_ptr = buffer;

    while ( *buf_ptr && read_strings < MAX_STRINGS ) {
        char temp_buf[MAX_BUFFER] = {0};
        char *temp_ptr = temp_buf;

        /*  Skip leading whitespace  */

        while ( *buf_ptr && isspace(*buf_ptr) ) {
            ++buf_ptr;
        }

        if ( *buf_ptr ) {
            if ( *buf_ptr == '"' ) {

                /*  If starts with '"', read to next '"'...  */

                ++buf_ptr;      /*  Skip first "  */
                while ( *buf_ptr && *buf_ptr != '"' ) {
                    *temp_ptr++ = *buf_ptr++;
                }

                if ( *buf_ptr ) {
                    ++buf_ptr;  /*  Skip second "  */
                }
            }
            else {

                /*  ...otherwise, read to next whitespace  */

                while ( *buf_ptr && !isspace(*buf_ptr) ) {
                    *temp_ptr++ = *buf_ptr++;
                }
            }

            /*  Copy substring into string array  */

            my_strings[read_strings] = malloc(strlen(temp_buf) + 1);
            if ( !my_strings[read_strings] ) {
                fprintf(stderr, "Couldn't allocate memory.\n");
                return EXIT_FAILURE;
            }
            strcpy(my_strings[read_strings++], temp_buf);
        }
    }

    for ( size_t i = 0; my_strings[i]; ++i ) {
        printf("String %zu: %s\n", i + 1, my_strings[i]);
        free(my_strings[i]);
    }

    return 0;
}

样本输出:

paul@MacBook:~/Documents/src/scratch$ ./ql
Boston "New York" "San Francisco" Memphis
String 1: Boston
String 2: New York
String 3: San Francisco
String 4: Memphis
paul@MacBook:~/Documents/src/scratch$ ./ql
a quoted "word" and "some quoted words" and an "unclosed quoted string
String 1: a
String 2: quoted
String 3: word
String 4: and
String 5: some quoted words
String 6: and
String 7: an
String 8: unclosed quoted string
paul@MacBook:~/Documents/src/scratch$ 

【讨论】:

  • +1 不错。次要注意事项:认为您需要'\"' 而不是'"',但我发现这并不总是正确的(§6.4.4.4 4)LSNED。在极少数情况下,size_t length = strlen(buffer); 返回 0 导致 UB 与 buffer[length - 1]。
【解决方案2】:

这是一个简单的实现:

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

int main(){
    char line[128] = "Boston \"New York\" \"San Francisco\" Memphis\n";
    //fgets(line, sizeof(line), stdin);//Boston "New York" "San Francisco" Memphis
    char array[10][32];
    char *p;
    int i, n, len;

    n = 0;
    for(p = line ; *p ; p += len){
        while(isspace(*p))//skip white space
            ++p;
        if(!*p)break;
        if(*p=='"'){
            sscanf(p, "\"%31[^\"]\"%n", array[n++], &len);
        } else {
            sscanf(p, "%31s%n", array[n++], &len);
        }
    }
    for(i=0;i<n;++i){
        printf("%s\n", array[i]);
    }
    return 0;
}

【讨论】:

    【解决方案3】:

    您可以将其分解为 2 个不同的问题:

    1. 如果您看到 ",则读取字符直到下一个 ",然后将引号之间的字符串复制到多维数组的下一个可用索引

    2. 读取 char 直到看到空格字符,然后将最后一个空格和当前空格之间的字符串(单词)复制到多维数组的下一个可用索引。

    这是假设您的输入格式正确并且每个引用都有对应的对。即它没有"Memphis 或类似的东西。

    【讨论】:

      【解决方案4】:

      为什么不在每个城市名称后面的字节分配 null?

      这将需要一个简单的算法,只传递一个 char 数组一次。 同时,您可以在指针数组中添加指向每个城市名称开头的指针。所以你得到了多维字符数组。

      唯一的问题——去掉引号。对原算法进行小改进就可以解决这个问题。

      【讨论】:

      • 这篇文章似乎没有回答这个问题(例如它是)。 OP 似乎在解析数据时遇到问题,因此 assign null to byte after each city name 没有帮助。
      猜你喜欢
      • 1970-01-01
      • 2013-09-12
      • 2015-08-07
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多