在C中去除前导和尾随空格的最佳方法是什么?
答案 0 :(得分:21)
以下是linux内核如何进行修剪,称为strstrip():
char *strstrip(char *s)
{
size_t size;
char *end;
size = strlen(s);
if (!size)
return s;
end = s + size - 1;
while (end >= s && isspace(*end))
end--;
*(end + 1) = '\0';
while (*s && isspace(*s))
s++;
return s;
}
它基本上是一个更好的格式化和错误检查版本,以前的海报说。
答案 1 :(得分:9)
这个问题看起来好像可能是一个家庭作业问题,所以我会斜着回答:查找isspace(3)和strlen(3)的手册页并使用指针算法。另外,根据手头的问题,您可能需要malloc(3)来保存结果空间。
不要忘记C字符串的表示包含一个尾随的0字节,通常写为'\ 0',不计算为字符串长度的一部分。
答案 2 :(得分:7)
这是使用isspace的版本:
char * trim(char *c) {
char * e = c + strlen(c) - 1;
while(*c && isspace(*c)) c++;
while(e > c && isspace(*e)) *e-- = '\0';
return c;
}
答案 3 :(得分:4)
你可以完全到位。
void stripLeadingAndTrailingSpaces(char* string){
assert(string);
/* First remove leading spaces */
const char* firstNonSpace = string;
while(*firstNonSpace != '\0' && isspace(*firstNonSpace))
{
++firstNonSpace;
}
size_t len = strlen(firstNonSpace)+1;
memmove(string, firstNonSpace, len);
/* Now remove trailing spaces */
char* endOfString = string + len;
while(string < endOfString && isspace(*endOfString))
{
--endOfString ;
}
*endOfString = '\0';
}
答案 4 :(得分:3)
char *strstrip(char *s)
{
char *end;
while ( (*s) && isspace( *s))
s++;
if(!( *s) )
return s;
end = s;
while( ! *end)
end++;
end--;
while (end ! = s && isspace( *end))
end--;
*(end + 1) = '\0';
return s;
}
它基本上是一个更优化的代码(在速度和代码化方面)。
如果我们需要保留内存空间,那么
void strstrip(char *s)
{
char *start;
char *end;
start = s;
while ( (*start) && isspace( *start))
start++;
if(!( *start) )
{
*s='\0';
return ;
}
end = start;
while( ! *end)
end++;
end--;
while (end ! = start && isspace( *end))
end--;
*(end + 1) = '\0';
memmove(s, start, end-start+1);
return;
}
答案 5 :(得分:3)
这是lakshmanaraj的第一个功能更简洁,更安全的版本:
#include <ctype.h>
char *mytrim(char *s)
{
if(s) { /* Don't forget to check for NULL! */
while(*s && isspace(*s))
++s;
if(*s) {
register char *p = s;
while(*p)
++p;
do {
--p;
} while((p != s) && isspace(*p));
*(p + 1) = '\0';
}
}
return(s);
}
答案 6 :(得分:2)
上述另一篇文章的改进。
void strstrip( char *s )
{
char *start;
char *end;
// Exit if param is NULL pointer
if (s == NULL)
return;
// Skip over leading whitespace
start = s;
while ((*start) && isspace(*start))
start++;
// Is string just whitespace?
if (!(*start))
{
*s = 0x00; // Truncate entire string
return;
}
// Find end of string
end = start;
while (*end)
end++;
// Step back from NUL
end--;
// Step backward until first non-whitespace
while ((end != start) && isspace(*end))
end--;
// Chop off trailing whitespace
*(end + 1) = 0x00;
// If had leading whitespace, then move entire string back to beginning
if (s != start)
memmove(s, start, end-start+1);
return;
}
答案 7 :(得分:2)
对于那些希望看到递归解决方案的人,我提供了这个:
static char* trim_left_ptr(char* str)
{
if (*str == 0)
{
// no more in string. It is an empty string
return str;
}
if (*str == ' ' || *str == '\t')
{
// it is space or tab. Try next.
return trim_left_ptr(str + 1);
}
// found left side of string
return str;
}
static char* trim_right_ptr(char* str)
{
if (*str == 0)
{
// found end of string
return str;
}
// try next (recursion)
char* ptr = trim_right_ptr( str + 1 );
// on the return from recursion.
// ptr == str until a nonspace/nontab is found.
if (*(ptr - 1) == ' ' || *(ptr - 1) == '\t')
{
// is space or tab. Have not yet found right side
return ptr - 1;
}
// found right side of string
return ptr;
}
char* trim(char* str)
{
char* L_Ptr = trim_left_ptr(str);
char* R_Ptr = trim_right_ptr(str);
// calculate characters to store in new buffer
_int32 sz = R_Ptr - L_Ptr;
// allocate buffer
char* newstr = (char*) malloc(sz + 1);
// copy trimmed string
memcpy(newstr, L_Ptr, sz);
// terminate string
*(newstr + sz) = 0;
return newstr;
}
当然,它不是唯一可能的递归解决方案。
答案 8 :(得分:2)
为已经拥挤的领域添加另一个答案,但......我相信有充分的理由。具体来说,answer的AlfaZulu({3}}既不会删除尾随空白,也不会正确地尊重数组的边界。当源字符串为空字符串时,Valgrind报告越界读写。
这是带有stripLeadingAndTrailingSpaces()
函数的示例代码,来自AlfaZulu的答案逐字(包括尾随空白) - 只有static
才能符合我的偏见。 (我使用阻止代码编译的编译器选项,除非有函数的原型或函数是静态的)。还有一个函数str_strip()
,它是AlfaZulu函数的固定版本。测试工具使两个功能起作用。该代码假定具有足够类似POSIX的环境,以便strdup()
可用于分配字符串的副本。
请注意,名称str_strip()
可避免与标准C库的保留名称冲突:
7.31.13字符串处理
<string.h>
可以添加以
str
,mem
或wcs
开头的小写字母和小写字母 到<string.h>
标题中的声明。
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
/* Code verbatim from answer by AlfaZulu (except for static) — which has problems */
static
void stripLeadingAndTrailingSpaces(char* string){
assert(string);
/* First remove leading spaces */
const char* firstNonSpace = string;
while(*firstNonSpace != '\0' && isspace(*firstNonSpace))
{
++firstNonSpace;
}
size_t len = strlen(firstNonSpace)+1;
memmove(string, firstNonSpace, len);
/* Now remove trailing spaces */
char* endOfString = string + len;
while(string < endOfString && isspace(*endOfString))
{
--endOfString ;
}
*endOfString = '\0';
}
static void str_strip(char *string)
{
assert(string);
//printf("-->> %s(): [%s]\n", __func__, string);
/* First remove leading spaces */
const char *firstNonSpace = string;
while (isspace((unsigned char)*firstNonSpace))
++firstNonSpace;
//printf("---- %s(): [%s]\n", __func__, firstNonSpace);
size_t len = strlen(firstNonSpace) + 1;
memmove(string, firstNonSpace, len);
//printf("---- %s(): [%s]\n", __func__, string);
/* Now remove trailing spaces */
char *endOfString = string + len - 1;
//printf("---- %s(): EOS [%s]\n", __func__, endOfString);
while (string < endOfString && isspace((unsigned char)endOfString[-1]))
--endOfString;
*endOfString = '\0';
//printf("<<-- %s(): [%s]\n", __func__, string);
}
static void chk_stripper(const char *str)
{
char *copy1 = strdup(str);
printf("V1 Before: [%s]\n", copy1);
stripLeadingAndTrailingSpaces(copy1);
printf("V1 After: [%s]\n", copy1);
free(copy1);
fflush(stdout);
char *copy2 = strdup(str);
printf("V2 Before: [%s]\n", copy2);
str_strip(copy2);
printf("V2 After: [%s]\n", copy2);
free(copy2);
fflush(stdout);
}
int main(void)
{
char *str[] =
{
" \t ABC DEF \t ",
" \t \t ",
" ",
"",
};
enum { NUM_STR = sizeof(str) / sizeof(str[0]) };
for (int i = 0; i < NUM_STR; i++)
chk_stripper(str[i]);
return 0;
}
在Valgrind下运行时,我得到输出:
$ valgrind --suppressions=etc/suppressions-macos-10.12.5 -- ./slts59
==26999== Memcheck, a memory error detector
==26999== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26999== Using Valgrind-3.13.0.SVN and LibVEX; rerun with -h for copyright info
==26999== Command: ./slts59
==26999==
V1 Before: [ ABC DEF ]
V1 After: [ABC DEF ]
V2 Before: [ ABC DEF ]
V2 After: [ABC DEF]
V1 Before: [ ]
V1 After: []
V2 Before: [ ]
V2 After: []
V1 Before: [ ]
V1 After: []
V2 Before: [ ]
V2 After: []
==26999== Invalid read of size 1
==26999== at 0x100000B81: stripLeadingAndTrailingSpaces (slts59.c:28)
==26999== by 0x100000CB0: chk_stripper (slts59.c:67)
==26999== by 0x100000DA2: main (slts59.c:91)
==26999== Address 0x100b7df01 is 0 bytes after a block of size 1 alloc'd
==26999== at 0x100096861: malloc (vg_replace_malloc.c:302)
==26999== by 0x1002DC938: strdup (in /usr/lib/system/libsystem_c.dylib)
==26999== by 0x100000C88: chk_stripper (slts59.c:65)
==26999== by 0x100000DA2: main (slts59.c:91)
==26999==
==26999== Invalid write of size 1
==26999== at 0x100000B96: stripLeadingAndTrailingSpaces (slts59.c:33)
==26999== by 0x100000CB0: chk_stripper (slts59.c:67)
==26999== by 0x100000DA2: main (slts59.c:91)
==26999== Address 0x100b7df01 is 0 bytes after a block of size 1 alloc'd
==26999== at 0x100096861: malloc (vg_replace_malloc.c:302)
==26999== by 0x1002DC938: strdup (in /usr/lib/system/libsystem_c.dylib)
==26999== by 0x100000C88: chk_stripper (slts59.c:65)
==26999== by 0x100000DA2: main (slts59.c:91)
==26999==
V1 Before: []
V1 After: []
V2 Before: []
V2 After: []
==26999==
==26999== HEAP SUMMARY:
==26999== in use at exit: 34,572 bytes in 162 blocks
==26999== total heap usage: 186 allocs, 24 frees, 40,826 bytes allocated
==26999==
==26999== LEAK SUMMARY:
==26999== definitely lost: 0 bytes in 0 blocks
==26999== indirectly lost: 0 bytes in 0 blocks
==26999== possibly lost: 0 bytes in 0 blocks
==26999== still reachable: 0 bytes in 0 blocks
==26999== suppressed: 34,572 bytes in 162 blocks
==26999==
==26999== For counts of detected and suppressed errors, rerun with: -v
==26999== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
$
显示的str_strip()
功能正常工作。它具有或多或少的必要更改,以使stripLeadingAndTrailingSpaces()
干净地工作(加上在检查过程中使用的注释掉的调试代码 - 现在可以进行)。
我观察到虽然它的语义略有不同,但Tuminoid的answer中基于Linux的strstrip()
函数也很干净 - 没有内存访问错误,它删除了尾随空白(不移动第一个和最后一个非空白字符之间的字符串部分。)
答案 9 :(得分:1)
int i = strlen(s) - 1;
while (isspace(s[i]))
s[i--] = '\0';
while (isspace(*s))
s++;
只要你不关心像疯了一样纠结字符串,如果你不关心内存泄漏,那就应该解决这个问题!
答案 10 :(得分:1)
你应该能够就地做到这一点;剥离空格永远不会导致字符串增长。你可以在没有先检查字符串长度的情况下做到这一点,但这样做可能是不必要的“聪明”。你应该查看memmove()
函数,以及@Norman Ramsey提到的函数。
答案 11 :(得分:1)
如果您使用的是Linux / Windows并将库glib链接到您的程序中,则可以使用例程g_strstrip()
。
答案 12 :(得分:1)
char *strip(char *string)
{
char *start = string;
while(isblank(*start)) start++;
int end = strlen(start);
if(start != string) {
memmove(string, start, end);
string[end] = '\0';
}
while(isblank(*(string+end-1))) end--;
string[end] = '\0';
return string;
}
答案 13 :(得分:1)
来自fpsgamer的校正算法(也是ISO C90有效):
void trimWhitespace(char *string) {
const char* firstNonSpace = string;
char* endOfString;
size_t len;
if (string[0] == '\0') {
return;
}
/* First remove leading spaces */
while(*firstNonSpace != '\0' && isspace(*firstNonSpace)) {
++firstNonSpace;
}
len = strlen(firstNonSpace) + 1;
memmove(string, firstNonSpace, len);
/* Now remove trailing spaces */
endOfString = string + len;
while(string < endOfString && (isspace(*endOfString) || *endOfString == '\0')) {
--endOfString ;
}
*(endOfString + 1) = '\0';
}
答案 14 :(得分:1)
用于尾随空格使用strtok。设置分隔符=&#34; &#34;当它运行时,它会丢弃分隔符字节并将char *返回给令牌
char *x;
char *y = "somestring ";
x = strtok(y," ");
结果x =指向&#34; somestring&#34;不是&#34; somestring&#34;
答案 15 :(得分:1)
修改:根据zString library的最新版本更新了代码。
此代码不依赖于任何库,只依赖于指针算术和整数。 有三个功能:修剪,左边修剪和右边修剪。 (我应该将所有这些功能添加到zString library :))
char *zstring_trim(char *s)
删除了前导和尾随空格
char *zstring_ltrim(char *s)
删除了前导空白
char *zstring_ltrim(char *s)
删除尾随白色空格
所有这些功能都会修改原始字符串
/* trim */
char *zstring_trim(char *str){
char *src=str; /* save the original pointer */
char *dst=str; /* result */
char c;
int is_space=0;
int in_word=0; /* word boundary logical check */
int index=0; /* index of the last non-space char*/
/* validate input */
if (!str)
return str;
while ((c=*src)){
is_space=0;
if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ')
is_space=1;
if(is_space == 0){
/* Found a word */
in_word = 1;
*dst++ = *src++; /* make the assignment first
* then increment
*/
} else if (is_space==1 && in_word==0) {
/* Already going through a series of white-spaces */
in_word=0;
++src;
} else if (is_space==1 && in_word==1) {
/* End of a word, dont mind copy white spaces here */
in_word=0;
*dst++ = *src++;
index = (dst-str)-1; /* location of the last char */
}
}
/* terminate the string */
*(str+index)='\0';
return str;
}
/* right trim */
char *zstring_rtrim(char *str){
char *src=str; /* save the original pointer */
char *dst=str; /* result */
char c;
int is_space=0;
int index=0; /* index of the last non-space char */
/* validate input */
if (!str)
return str;
/* copy the string */
while(*src){
*dst++ = *src++;
c = *src;
if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ')
is_space=1;
else
is_space=0;
if (is_space==0 && *src)
index = (src-str)+1;
}
/* terminate the string */
*(str+index)='\0';
return str;
}
/* left trim */
char *zstring_ltrim(char *str){
char *src=str; /* save the original pointer */
char *dst=str; /* result */
char c;
int index=0; /* index of the first non-space char */
/* validate input */
if (!str)
return str;
/* skip leading white-spaces */
while((c=*src)){
if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' '){
++src;
++index;
} else
break;
}
/* copy rest of the string */
while(*src)
*dst++ = *src++;
/* terminate the string */
*(src-index)='\0';
return str;
}