删除字符串开始及末尾的空白符,并且把数组中间的多个空格(如果有)符转化(合并)为1个

这是一道面试题:

删除字符串开始及末尾的空白符,并且把数组中间的多个空格(如果有)符转化(合并)为1个。例如把“ I    like   python     !  "变成”I like python !“。

问题很明确,关键是要减少字符的移动次数。要减少字符的移动次数,如果能一次性就把字符移到准确地位置就好了,不要做不必要的移动。下面给出用python些的简单代码。

#!/usr/bin/python

import sys

def rmblank(blankstr):
        new_start=0
        i=0
        blankspace=False
        while i<len(blankstr):
                #move behind chars to new start point
                if blankstr[i]!=' ':
                        blankstr[new_start]=blankstr[i]
                        new_start+=1
                        blankspace=True
                else:
                        #if more than one blank, move behind chars to new start point
                        if blankspace==True:
                                blankstr[new_start]=blankstr[i]
                                new_start+=1
                                blankspace=False
                i+=1
        return blankstr[0:new_start]
def main():
        blankstr=" I like   python   ! "
        print blankstr
        print ''.join(rmblank(list(blankstr)))
if __name__=='__main__':
        main()

参考文献:

Jude Zou的http://www.oschina.net/code/snippet_142087_4696

<think>我们已知用户的问题:逐个读取用空格隔开的字符串,且不知道字符串数量。 根据引用[1][2]的提示,scanf在读取字符串时遇到空格会停止,因此我们可以利用scanf的%s格式来读取以空格分隔的每个字符串(因为每个字符串之间用空格隔开,所以每个字符串本身不包含空格)。 但是,用户要求逐个读取,且不知道数量,所以我们需要循环读取直到输入结束。 方法: 1. 使用scanf循环读取,每次读取一个字符串(直到遇到空格或换行,注意:换行也会结束当前字符串)。 2. 但是,当输入结束时(比如遇到文件结束EOF),scanf会返回EOF,我们可以利用这一点来控制循环。 具体步骤: 我们可以使用: while (scanf("%s", str) == 1) { // 处理这个字符串 } 解释: - scanf的%s会跳过前面的空白字(包括空格、制表、换行),然后读取非空白字直到再次遇到空白或文件结束。 - 每次调用scanf读取一个字符串,成功读取则返回1(因为只有一个%s),如果遇到文件结束则返回EOF(通常为-1),所以条件为==1。 注意:这种方法不能读取包含空格字符串(因为每个字符串空格隔开,所以每个字符串内部不能有空格,否则会被拆成多个字符串)。如果字符串内部需要包含空格,则不能使用这种方法。但根据问题描述,每个字符串空格隔开,所以内部不含空格。 但是,引用[1]提到gets函数不安全,引用[2]提到fgets可以读取包含空格字符串,但这里我们不需要,因为每个字符串是不含空格的。 另外,我们不知道字符串数量,所以循环读取直到结束。 还有一个问题:输入可能来自文件重定向,也可能来自键盘。对于键盘输入,如何表示结束?在Linux/Unix下按Ctrl+D,Windows下按Ctrl+Z。 代码示例: ```c #include <stdio.h> #include <stdlib.h> #define MAX_STR_LEN 100 // 假设每个字符串最大长度为99(留一个位置给'\0') int main() { char str[MAX_STR_LEN]; printf("请输入用空格分隔的字符串(按Ctrl+D结束输入):\n"); // 循环读取,直到遇到文件结束或输入错误 while (scanf("%99s", str) == 1) { // 使用%99s防止溢出,注意数组大小是100,所以写99 // 处理每个字符串,例如打印出来 printf("读取到: %s\n", str); } return 0; } ``` 注意事项: 1. 为防止缓冲区溢出,我们在格式字符串中指定了最大宽度为99(因为str数组大小为100,需要留一个位置给'\0')。这样即使输入超过99个字,也只会读99个,剩下的留在缓冲区,可能会被下一个字符串读取(但如果下一个字符串也是用同样的方法读取,则会继续读剩下的,可能造成非预期的结果)。如果有可能超过99,那么需要更安全的处理,比如动态分配或者使用更复杂的解析。 2. 如果输入行的末尾有换行,那么最后一个字符串读取后,下一个scanf会跳过换行(作为空白),然后等待下一行输入(如果键盘输入的话)或者遇到EOF(如果是文件输入结尾)。 3. 这个方法在输入结束时(按Ctrl+D或Ctrl+Z)会退出循环。 4. 如果用户输入的一行中包含多个空格,scanf的%s会忽略前导空格,所以多个空格会被视为一个分隔(即连续空格仍然能正确分隔)。 5. 如果用户输入的一行结束(换行),那么scanf会继续读取下一行(因为换行也是空白字,会被跳过),直到遇到非空白字开始下一个字符串,或者遇到文件结束。 6. 如果希望每次读取一行,然后在一行内逐个处理字符串,那么可以使用fgets先读取一行,再用strtok分割(但题目要求逐个读取,且不知道数量,所以这种方法也可以)。 但是,根据问题,只需要逐个读取,用空格分隔,所以上述方法已经满足。 另外,如果用户希望动态存储这些字符串(因为不知道个数,所以需要动态数组或者链表),那么还需要动态分配内存来存储每个字符串。 下面是一个动态分配内存存储所有字符串的示例(使用动态数组,即指针数组,并动态扩展数组大小): ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define INITIAL_CAPACITY 10 // 初始容量 #define MAX_STR_LEN 100 int main() { char buffer[MAX_STR_LEN]; char **strings = NULL; // 指向字符串指针数组的指针 int capacity = INITIAL_CAPACITY; // 当前数组容量 int count = 0; // 当前字符串个数 // 分配初始指针数组 strings = (char **)malloc(capacity * sizeof(char *)); if (strings == NULL) { perror("malloc"); exit(1); } printf("请输入用空格分隔的字符串(按Ctrl+D结束输入):\n"); while (scanf("%99s", buffer) == 1) { // 如果数组已满,扩容 if (count >= capacity) { capacity *= 2; // 扩容一倍 char **temp = (char **)realloc(strings, capacity * sizeof(char *)); if (temp == NULL) { perror("realloc"); // 注意:这里不能直接退出,需要释放之前分配的内存 for (int i = 0; i < count; i++) { free(strings[i]); } free(strings); exit(1); } strings = temp; } // 为当前字符串分配内存并复制 strings[count] = (char *)malloc(strlen(buffer) + 1); if (strings[count] == NULL) { perror("malloc"); // 同样,需要释放之前分配的内存 for (int i = 0; i < count; i++) { free(strings[i]); } free(strings); exit(1); } strcpy(strings[count], buffer); count++; } // 打印所有读取的字符串 printf("总共读取了%d个字符串:\n", count); for (int i = 0; i < count; i++) { printf("%s\n", strings[i]); } // 释放内存 for (int i = 0; i < count; i++) { free(strings[i]); } free(strings); return 0; } ``` 这个示例演示了如何动态存储所有字符串。如果不需要存储所有字符串,只是逐个处理(比如打印),那么第一个简单示例就足够了。 注意:在读取过程中,我们假设每个字符串最大长度为99(包括结尾的'\0'),所以使用buffer作为中间存储。如果字符串可能更长,那么需要动态调整buffer的大小,但题目没有说明,我们假设有最大长度限制。 引用[2]提到scanf在遇到空格时会停止读取,这正合我们的需求(因为我们要逐个读取以空格分隔的字符串)[^2]。 因此,我们推荐使用scanf循环读取的方法。 相关问题: 1. 如何读取包含空格的单个字符串? 2. 如何使用fgetsstrtok来分割空格分隔的字符串? 3. 如何处理输入中可能出现的连续多个空格? 4. 在动态内存分配的示例中,如何避免内存泄漏? </think>### C语言逐个读取空格分隔字符串的实现方法 在C语言中逐个读取用空格分隔的未知数量字符串,可以采用以下方法: #### 方法1:使用`scanf()`循环读取(推荐) ```c #include <stdio.h> #include <stdlib.h> int main() { char word[100]; // 假设每个单词最大长度99字 printf("输入空格分隔的字符串(Ctrl+D结束):\n"); // 循环读取直到输入结束 while (scanf("%99s", word) == 1) { // %99s防止缓冲区溢出 printf("读取到: %s\n", word); } return 0; } ``` **工作原理**: 1. `%s`格式自动跳过前导空白字空格、制表等) 2. 读取连续的非空白字直到遇到空格/换行/EOF 3. 返回1表示成功读取一个单词[^2] 4. 当输入结束(Ctrl+D或文件尾)时循环终止 **特点**: - 自动处理连续多个空格 - 高效简洁的内存使用 - 支持任意数量的输入字符串 - 缓冲区保护防止溢出[^2] #### 方法2:使用`fgets()`配合`strtok()`分割 ```c #include <stdio.h> #include <string.h> int main() { char input[1024]; // 输入缓冲区 printf("输入一行字符串: "); fgets(input, sizeof(input), stdin); // 安全读取整行[^2] // 分割字符串 char *token = strtok(input, " \n"); // 空格换行作为分隔 while (token != NULL) { printf("读取到: %s\n", token); token = strtok(NULL, " \n"); // 继续分割剩余部分 } return 0; } ``` **适用场景**: - 需要先获取完整输入行再处理 - 要求处理包含特殊字的输入 - 适合交互式终端输入 ### 关键要点 1. **动态内存处理**:如需存储所有字符串,可结合`malloc`动态扩展数组 ```c char **words = NULL; int count = 0; while (scanf("%99s", buffer) == 1) { words = realloc(words, (count+1)*sizeof(char*)); words[count] = strdup(buffer); count++; } ``` 2. **空白处理**: - 连续空格会被视为单个分隔 - 行首/行尾空格自动忽略 - 换行`\n`会被`scanf`的`%s`跳过[^2] 3. **安全建议**: - 始终指定`scanf`的读取宽度(如`%99s`) - 对于未知长度的输入,优先使用`fgets`[^2] - 检查内存分配结果 4. **结束条件**: - 控制台:Windows按`Ctrl+Z`,Linux/macOS按`Ctrl+D` - 文件输入:自动检测EOF ### 性能对比 | 方法 | 优点 | 缺点 | |------------|--------------------------|--------------------------| | `scanf`循环| 内存效率高,代码简洁 | 不能读取含空格字符串 | | `fgets`+分割| 可处理任意字,更安全 | 需要额外缓冲区 | > 对于纯空格分隔的单词序列,推荐`scanf`循环法,因其简洁高效且支持无限输入[^1][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值