从 C 中的字符串中弹出多余的 space
Eject excess space from string in C
我需要编写一个函数来从 C 中的字符串中弹出多余的 space。
示例:
char s[]=" abcde abcde ";
输出:
"abcde abcde"
代码:
#include <stdio.h>
#include <ctype.h>
char *eject(char *str) {
int i, x;
for (i = x = 0; str[i]; ++i)
if (!isspace(str[i]) || (i > 0 && !isspace(str[i - 1])))
str[x++] = str[i];
if(x > 0 && str[x-1] == ' ') str[x-1] = '[=13=]';
return str;
}
int main() {
char s[] = " abcde abcde ";
printf("\"%s\"", eject(s));
return 0;
}
此代码不适用于字符串 " "
如果找到这个字符串,程序应该打印:
""
如何解决这个问题?
您可以编写两个 trim 前导和尾随空白字符的函数。
void trim_front(char *src) {
size_t i = 0, j = 0;
while (isspace(src[i])) i++;
while (i < strlen(src)) src[j++] = src[i++];
src[j] = '[=10=]';
}
void trim_back(char *src) {
char *ch = src + strlen(src) - 1;
while (isspace(*ch)) *ch-- = '[=10=]';
}
如果您知道不必处理尾随空格或前导空格,您的任务就会变得简单得多。
void reduce_spaces(char *src) {
size_t i = 0, j = 0;
for (; i < strlen(src); ++i) {
if (i == strlen(src) - 1 ||
(isspace(src[i]) && !isspace(src[i + 1])) ||
!isspace(src[i])) {
src[j++] = src[i];
}
}
src[j] = '[=11=]';
}
并测试这个:
int main(void) {
char s[] = " hello world ";
trim_front(s);
trim_back(s);
reduce_spaces(s);
printf(">%s<\n", s);
return 0;
}
% gcc test.c
% ./a.out
>hello world<
%
当然,如果你真的想,你可以将这些函数的代码移植到reduce_spaces
,但是将一个问题分解成多个更小的问题可以使事情变得容易得多。
基本上,您需要删除输入字符串中单词之间的连续 space 个字符以及输入字符串的所有前导和尾随 space 个字符。这意味着,编写代码以删除输入字符串中连续的 space 个字符,并在删除连续的 space 个字符的同时,完全删除前导和尾随 space 个字符。
您只需一次迭代即可完成。无需编写用于删除输入字符串前导和尾随 space 的不同函数,如其他 post.
所示
你可以这样做:
#include <stdio.h>
#include <ctype.h>
char * eject (char *str) {
if (str == NULL) {
printf ("Invalid input..\n");
return NULL;
}
/* Pointer to keep track of position where next character to be write
*/
char * p = str;
for (unsigned int i = 0; str[i] ; ++i) {
if ((isspace (str[i])) && ((p == str) || (str[i + 1] == '[=10=]') || (str[i] == (str[i + 1])))) {
continue;
}
*p++ = str[i];
}
/* Add the null terminating character.
*/
*p = '[=10=]';
return str;
}
int main (void) {
char s[] = " abcde abcde ";
printf("\"%s\"\n", eject(s));
char s1[] = " ";
printf("\"%s\"\n", eject(s1));
char s2[] = "ab yz ";
printf("\"%s\"\n", eject(s2));
char s3[] = " ddd xx jj m";
printf("\"%s\"\n", eject(s3));
char s4[] = "";
printf("\"%s\"\n", eject(s4));
return 0;
}
输出:
# ./a.out
"abcde abcde"
""
"ab yz"
"ddd xx jj m"
""
一个稍微高级的答案,仅供参考 - 假设您的任务是编写一个专业的库,供在现实世界的程序中使用。然后首先列出所有有意义的要求:
- 将字符串视为“不可变”是一种很好的做法 - 也就是说,基于旧字符串而不是 in-place 替换来构建新字符串。
- 将目标字符串作为参数,同时 return 指向它的指针(类似于
strcpy
等函数)。
- 如果是空字符串,也将目标字符串设置为空。
- 删除所有“白色 space”,而不仅仅是
' '
字符。
- 与其总是在每个单词后插入一个 space 字符,不如插入一个可变定界符?也可能是
,
或 ;
.
- 最后一个词后不应插入定界符。
- 出于性能原因,该算法应该只遍历一次数据。也就是说,像
strlen
等内部调用是不可接受的。
- 逐字节迭代很好 - 我们不需要关心对齐。
那么我们可能会想到这样的事情:
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
char* trim_delimit (char* restrict dst, const char* restrict src, char delim)
{
char* start = dst;
*dst = '[=10=]';
bool remove_spaces = true;
char* insert_delim_pos = NULL;
for(; *src != '[=10=]'; src++)
{
if(remove_spaces)
{
if(!isspace(*src))
{
remove_spaces = false;
if(insert_delim_pos != NULL)
{
// we only get here if more words were found, not yet at the end of the string
*insert_delim_pos = delim;
insert_delim_pos = NULL;
}
}
}
if(!remove_spaces)
{
if(isspace(*src))
{
remove_spaces = true;
insert_delim_pos = dst; // remember where to insert delimiter for later
}
else
{
*dst = *src;
}
dst++;
}
}
return start;
}
测试用例:
int main (void)
{
char s[]=" abcde abcde ";
char trimmed[100];
puts(trim_delimit(trimmed, s, ' '));
puts(trim_delimit(trimmed, "", ' '));
puts(trim_delimit(trimmed, s, ';'));
}
输出:
abcde abcde
abcde;abcde
我需要编写一个函数来从 C 中的字符串中弹出多余的 space。
示例:
char s[]=" abcde abcde ";
输出:
"abcde abcde"
代码:
#include <stdio.h>
#include <ctype.h>
char *eject(char *str) {
int i, x;
for (i = x = 0; str[i]; ++i)
if (!isspace(str[i]) || (i > 0 && !isspace(str[i - 1])))
str[x++] = str[i];
if(x > 0 && str[x-1] == ' ') str[x-1] = '[=13=]';
return str;
}
int main() {
char s[] = " abcde abcde ";
printf("\"%s\"", eject(s));
return 0;
}
此代码不适用于字符串 " "
如果找到这个字符串,程序应该打印:
""
如何解决这个问题?
您可以编写两个 trim 前导和尾随空白字符的函数。
void trim_front(char *src) {
size_t i = 0, j = 0;
while (isspace(src[i])) i++;
while (i < strlen(src)) src[j++] = src[i++];
src[j] = '[=10=]';
}
void trim_back(char *src) {
char *ch = src + strlen(src) - 1;
while (isspace(*ch)) *ch-- = '[=10=]';
}
如果您知道不必处理尾随空格或前导空格,您的任务就会变得简单得多。
void reduce_spaces(char *src) {
size_t i = 0, j = 0;
for (; i < strlen(src); ++i) {
if (i == strlen(src) - 1 ||
(isspace(src[i]) && !isspace(src[i + 1])) ||
!isspace(src[i])) {
src[j++] = src[i];
}
}
src[j] = '[=11=]';
}
并测试这个:
int main(void) {
char s[] = " hello world ";
trim_front(s);
trim_back(s);
reduce_spaces(s);
printf(">%s<\n", s);
return 0;
}
% gcc test.c
% ./a.out
>hello world<
%
当然,如果你真的想,你可以将这些函数的代码移植到reduce_spaces
,但是将一个问题分解成多个更小的问题可以使事情变得容易得多。
基本上,您需要删除输入字符串中单词之间的连续 space 个字符以及输入字符串的所有前导和尾随 space 个字符。这意味着,编写代码以删除输入字符串中连续的 space 个字符,并在删除连续的 space 个字符的同时,完全删除前导和尾随 space 个字符。
您只需一次迭代即可完成。无需编写用于删除输入字符串前导和尾随 space 的不同函数,如其他 post.
所示你可以这样做:
#include <stdio.h>
#include <ctype.h>
char * eject (char *str) {
if (str == NULL) {
printf ("Invalid input..\n");
return NULL;
}
/* Pointer to keep track of position where next character to be write
*/
char * p = str;
for (unsigned int i = 0; str[i] ; ++i) {
if ((isspace (str[i])) && ((p == str) || (str[i + 1] == '[=10=]') || (str[i] == (str[i + 1])))) {
continue;
}
*p++ = str[i];
}
/* Add the null terminating character.
*/
*p = '[=10=]';
return str;
}
int main (void) {
char s[] = " abcde abcde ";
printf("\"%s\"\n", eject(s));
char s1[] = " ";
printf("\"%s\"\n", eject(s1));
char s2[] = "ab yz ";
printf("\"%s\"\n", eject(s2));
char s3[] = " ddd xx jj m";
printf("\"%s\"\n", eject(s3));
char s4[] = "";
printf("\"%s\"\n", eject(s4));
return 0;
}
输出:
# ./a.out
"abcde abcde"
""
"ab yz"
"ddd xx jj m"
""
一个稍微高级的答案,仅供参考 - 假设您的任务是编写一个专业的库,供在现实世界的程序中使用。然后首先列出所有有意义的要求:
- 将字符串视为“不可变”是一种很好的做法 - 也就是说,基于旧字符串而不是 in-place 替换来构建新字符串。
- 将目标字符串作为参数,同时 return 指向它的指针(类似于
strcpy
等函数)。 - 如果是空字符串,也将目标字符串设置为空。
- 删除所有“白色 space”,而不仅仅是
' '
字符。 - 与其总是在每个单词后插入一个 space 字符,不如插入一个可变定界符?也可能是
,
或;
. - 最后一个词后不应插入定界符。
- 出于性能原因,该算法应该只遍历一次数据。也就是说,像
strlen
等内部调用是不可接受的。 - 逐字节迭代很好 - 我们不需要关心对齐。
那么我们可能会想到这样的事情:
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
char* trim_delimit (char* restrict dst, const char* restrict src, char delim)
{
char* start = dst;
*dst = '[=10=]';
bool remove_spaces = true;
char* insert_delim_pos = NULL;
for(; *src != '[=10=]'; src++)
{
if(remove_spaces)
{
if(!isspace(*src))
{
remove_spaces = false;
if(insert_delim_pos != NULL)
{
// we only get here if more words were found, not yet at the end of the string
*insert_delim_pos = delim;
insert_delim_pos = NULL;
}
}
}
if(!remove_spaces)
{
if(isspace(*src))
{
remove_spaces = true;
insert_delim_pos = dst; // remember where to insert delimiter for later
}
else
{
*dst = *src;
}
dst++;
}
}
return start;
}
测试用例:
int main (void)
{
char s[]=" abcde abcde ";
char trimmed[100];
puts(trim_delimit(trimmed, s, ' '));
puts(trim_delimit(trimmed, "", ' '));
puts(trim_delimit(trimmed, s, ';'));
}
输出:
abcde abcde
abcde;abcde