답변:
문자열을 수정할 수있는 경우 :
// Note: This function returns a pointer to a substring of the original string.
// If the given string was allocated dynamically, the caller must not overwrite
// that pointer with the returned value, since the original pointer must be
// deallocated using the same allocator with which it was allocated. The return
// value must NOT be deallocated using free() etc.
char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
문자열을 수정할 수 없으면 기본적으로 동일한 방법을 사용할 수 있습니다.
// Stores the trimmed input string into the given output buffer, which must be
// large enough to store the result. If it is too small, the output is
// truncated.
size_t trimwhitespace(char *out, size_t len, const char *str)
{
if(len == 0)
return 0;
const char *end;
size_t out_size;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
{
*out = 0;
return 1;
}
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
end++;
// Set output size to minimum of trimmed string length and buffer size minus 1
out_size = (end - str) < len-1 ? (end - str) : len-1;
// Copy trimmed string and add null terminator
memcpy(out, str, out_size);
out[out_size] = 0;
return out_size;
}
str
는 지역 변수이며,이를 변경해도 전달되는 원래 포인터는 변경되지 않습니다. C의 함수 호출은 항상 값으로 전달되며 참조로 전달되지 않습니다.
free()
함수 의 유효한 인수 일 필요는 없습니다 . 반대의 경우-효율성을 위해 메모리 할당이 필요하지 않도록 설계했습니다. 전달 된 주소가 동적으로 할당 된 경우 호출자는 여전히 해당 메모리를 해제해야하므로 호출자는 해당 값을 여기에 리턴 된 값으로 겹쳐 쓰지 않아야합니다.
isspace
하는 unsigned char
, 그렇지 않으면 당신은 정의되지 않은 동작을 호출.
다음은 문자열을 버퍼의 첫 번째 위치로 이동시키는 것입니다. 문자열을 동적으로 할당 한 경우 trim ()이 반환하는 동일한 포인터에서 문자열을 해제 할 수 있도록이 동작을 원할 수 있습니다.
char *trim(char *str)
{
size_t len = 0;
char *frontp = str;
char *endp = NULL;
if( str == NULL ) { return NULL; }
if( str[0] == '\0' ) { return str; }
len = strlen(str);
endp = str + len;
/* Move the front and back pointers to address the first non-whitespace
* characters from each end.
*/
while( isspace((unsigned char) *frontp) ) { ++frontp; }
if( endp != frontp )
{
while( isspace((unsigned char) *(--endp)) && endp != frontp ) {}
}
if( frontp != str && endp == frontp )
*str = '\0';
else if( str + len - 1 != endp )
*(endp + 1) = '\0';
/* Shift the string so that it starts at str so that if it's dynamically
* allocated, we can still free it on the returned pointer. Note the reuse
* of endp to mean the front of the string buffer now.
*/
endp = str;
if( frontp != str )
{
while( *frontp ) { *endp++ = *frontp++; }
*endp = '\0';
}
return str;
}
정확성을 테스트하십시오.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* Paste function from above here. */
int main()
{
/* The test prints the following:
[nothing to trim] -> [nothing to trim]
[ trim the front] -> [trim the front]
[trim the back ] -> [trim the back]
[ trim front and back ] -> [trim front and back]
[ trim one char front and back ] -> [trim one char front and back]
[ trim one char front] -> [trim one char front]
[trim one char back ] -> [trim one char back]
[ ] -> []
[ ] -> []
[a] -> [a]
[] -> []
*/
char *sample_strings[] =
{
"nothing to trim",
" trim the front",
"trim the back ",
" trim front and back ",
" trim one char front and back ",
" trim one char front",
"trim one char back ",
" ",
" ",
"a",
"",
NULL
};
char test_buffer[64];
char comparison_buffer[64];
size_t index, compare_pos;
for( index = 0; sample_strings[index] != NULL; ++index )
{
// Fill buffer with known value to verify we do not write past the end of the string.
memset( test_buffer, 0xCC, sizeof(test_buffer) );
strcpy( test_buffer, sample_strings[index] );
memcpy( comparison_buffer, test_buffer, sizeof(comparison_buffer));
printf("[%s] -> [%s]\n", sample_strings[index],
trim(test_buffer));
for( compare_pos = strlen(comparison_buffer);
compare_pos < sizeof(comparison_buffer);
++compare_pos )
{
if( test_buffer[compare_pos] != comparison_buffer[compare_pos] )
{
printf("Unexpected change to buffer @ index %u: %02x (expected %02x)\n",
compare_pos, (unsigned char) test_buffer[compare_pos], (unsigned char) comparison_buffer[compare_pos]);
}
}
}
return 0;
}
소스 파일은 trim.c입니다. 'cc -Wall trim.c -o trim'으로 컴파일되었습니다.
isspace
하는 unsigned char
, 그렇지 않으면 당신은 정의되지 않은 동작을 호출.
isspace()
왜 호출 합니까 " "
와 "\n"
? 나는 줄 바꿈에 대한 단위 테스트를 추가하며 ... 나에게 확인을 보이는 ideone.com/bbVmqo
*(endp + 1) = '\0';
. 답에 대한 예제 테스트는이 문제를 피하는 64 버퍼를 사용합니다.
내 솔루션. 문자열은 변경 가능해야합니다. 다른 솔루션 중 일부는 공간이 아닌 부분을 처음으로 이동시켜 나중에 포인터를 free () 해야하는 경우 이전 포인터를 계속 사용할 수 있다는 이점이 있습니다.
void trim(char * s) {
char * p = s;
int l = strlen(p);
while(isspace(p[l - 1])) p[--l] = 0;
while(* p && isspace(* p)) ++p, --l;
memmove(s, p, l + 1);
}
이 버전에서는 문자열을 편집하는 대신 strndup ()을 사용하여 문자열 복사본을 만듭니다. strndup ()에는 _GNU_SOURCE가 필요하므로 malloc () 및 strncpy ()를 사용하여 자체 strndup ()을 만들어야 할 수도 있습니다.
char * trim(char * s) {
int l = strlen(s);
while(isspace(s[l - 1])) --l;
while(* s && isspace(* s)) ++s, --l;
return strndup(s, l);
}
trim()
발동 할 UB는 경우 s
입니다 ""
처음으로 isspace()
호출 될 것 isspace(p[-1])
와 p[-1]
반드시 법적 위치를 참조하지 않습니다.
isspace
하는 unsigned char
, 그렇지 않으면 당신은 정의되지 않은 동작을 호출.
if(l==0)return;
길이가 0 인 str을 피하기 위해 추가해야합니다
다음은 왼쪽, 오른쪽, 모두, 전체 및 장소에서 트리밍하고 지정된 문자 세트 (또는 기본적으로 공백)를 트리밍하는 C 미니 라이브러리입니다.
#ifndef STRLIB_H_
#define STRLIB_H_ 1
enum strtrim_mode_t {
STRLIB_MODE_ALL = 0,
STRLIB_MODE_RIGHT = 0x01,
STRLIB_MODE_LEFT = 0x02,
STRLIB_MODE_BOTH = 0x03
};
char *strcpytrim(char *d, // destination
char *s, // source
int mode,
char *delim
);
char *strtriml(char *d, char *s);
char *strtrimr(char *d, char *s);
char *strtrim(char *d, char *s);
char *strkill(char *d, char *s);
char *triml(char *s);
char *trimr(char *s);
char *trim(char *s);
char *kill(char *s);
#endif
#include <strlib.h>
char *strcpytrim(char *d, // destination
char *s, // source
int mode,
char *delim
) {
char *o = d; // save orig
char *e = 0; // end space ptr.
char dtab[256] = {0};
if (!s || !d) return 0;
if (!delim) delim = " \t\n\f";
while (*delim)
dtab[*delim++] = 1;
while ( (*d = *s++) != 0 ) {
if (!dtab[0xFF & (unsigned int)*d]) { // Not a match char
e = 0; // Reset end pointer
} else {
if (!e) e = d; // Found first match.
if ( mode == STRLIB_MODE_ALL || ((mode != STRLIB_MODE_RIGHT) && (d == o)) )
continue;
}
d++;
}
if (mode != STRLIB_MODE_LEFT && e) { // for everything but trim_left, delete trailing matches.
*e = 0;
}
return o;
}
// perhaps these could be inlined in strlib.h
char *strtriml(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_LEFT, 0); }
char *strtrimr(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_RIGHT, 0); }
char *strtrim(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_BOTH, 0); }
char *strkill(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_ALL, 0); }
char *triml(char *s) { return strcpytrim(s, s, STRLIB_MODE_LEFT, 0); }
char *trimr(char *s) { return strcpytrim(s, s, STRLIB_MODE_RIGHT, 0); }
char *trim(char *s) { return strcpytrim(s, s, STRLIB_MODE_BOTH, 0); }
char *kill(char *s) { return strcpytrim(s, s, STRLIB_MODE_ALL, 0); }
하나의 주요 루틴이 모든 것을 수행합니다. src == dst 인 경우 제자리 에서 잘립니다. 그렇지 않으면 strcpy
루틴 처럼 작동 합니다. 문자열 delim에 지정된 문자 세트를 자릅니다.이거나 공백 인 경우 공백입니다. 왼쪽, 오른쪽, 둘 다 및 모두 (tr과 같이) 다듬습니다. 그다지 많지 않고 문자열을 한 번만 반복합니다. 일부 사람들은 오른쪽에서 트림 시작이 왼쪽에서 시작된다고 불평 할 수 있지만 어쨌든 왼쪽에서 시작되는 strlen이 필요하지 않습니다. (오른쪽 손질을 위해 줄 끝까지 가야하는 한 가지 방법이 있습니다. 따라서 작업을 수행 할 수도 있습니다.) 파이프 라이닝 및 캐시 크기 등에 대해서는 논란이있을 수 있습니다. . 솔루션은 왼쪽에서 오른쪽으로 작동하고 한 번만 반복되므로 스트림에서도 작동하도록 확장 할 수 있습니다. 제한 사항 : 유니 코드 문자열 에서는 작동 하지 않습니다 .
dtab[*d]
캐스트하지 않습니다 . 서명 된 char가있는 시스템에서이 내용을 읽은 다음 버그가 발생하여 충돌이 발생할 수 있습니다. *d
unsigned int
dtab[-127]
dtab[*delim++]
하므로 정의되지 않은 잠재적 동작입니다 . 코드는 8 비트를 가정합니다 . 로 선언해야합니다 . 로 더 명확합니다 . 이 코드는 UTF-8 인코딩 문자열에서 작동하지만 비 ASCII 간격 시퀀스는 제거하지 않습니다. char
unsigned char
char
delim
const char *
dtab[0xFF & (unsigned int)*d]
dtab[(unsigned char)*d]
다음은 간단하지만 올바른 인플레 이스 트림 기능을 시도한 것입니다.
void trim(char *str)
{
int i;
int begin = 0;
int end = strlen(str) - 1;
while (isspace((unsigned char) str[begin]))
begin++;
while ((end >= begin) && isspace((unsigned char) str[end]))
end--;
// Shift all characters back to the start of the string array.
for (i = begin; i <= end; i++)
str[i - begin] = str[i];
str[i - begin] = '\0'; // Null terminate string.
}
while ((end >= begin) && isspace(str[end]))
경우 UB를 방지 하도록 변경하십시오 . str is
. Prevents
isspace
하는 unsigned char
, 그렇지 않으면 당신은 정의되지 않은 동작을 호출.
<ctype.h>
int와 함께 작동하도록 고안되었으며, 이는 int unsigned char
또는 special value 를 나타냅니다 EOF
. stackoverflow.com/q/7131026/225757을 참조하십시오 .
트림 파티에 늦게
특징 :
1. 많은 다른 답변에서와 같이 시작을 빨리 다듬습니다.
2. 끝까지 가고 나면 루프 당 하나의 테스트만으로 오른쪽을 트리밍합니다. @ jfm3과 유사하지만 모든 공백 문자열에 대해 작동합니다.)
3. char
부호있는 char
일 때 정의되지 않은 동작을 피하려면로 캐스팅 *s
하십시오 unsigned char
.
문자 처리 "모든 경우에 인수는
int
의 값이며, 그 값은unsigned char
매크로의 값 으로 나타내 거나 매크로의 값과 같아야합니다EOF
. 인수에 다른 값이 있으면 동작이 정의되지 않습니다." C11 §7.4 1
#include <ctype.h>
// Return a pointer to the trimmed string
char *string_trim_inplace(char *s) {
while (isspace((unsigned char) *s)) s++;
if (*s) {
char *p = s;
while (*p) p++;
while (isspace((unsigned char) *(--p)));
p[1] = '\0';
}
// If desired, shift the trimmed string
return s;
}
@chqrlie 는 위에서 잘린 문자열을 이동시키지 않는다고 언급했습니다. 그렇게하려면 ....
// Return a pointer to the (shifted) trimmed string
char *string_trim_inplace(char *s) {
char *original = s;
size_t len = 0;
while (isspace((unsigned char) *s)) {
s++;
}
if (*s) {
char *p = s;
while (*p) p++;
while (isspace((unsigned char) *(--p)));
p[1] = '\0';
// len = (size_t) (p - s); // older errant code
len = (size_t) (p - s + 1); // Thanks to @theriver
}
return (s == original) ? s : memmove(original, s, len + 1);
}
다음은 @ adam-rosenfields 내부 수정 루틴과 유사하지만 strlen ()에 불필요하게 의존하지 않는 솔루션입니다. @jkramer와 마찬가지로 문자열은 버퍼 내에서 왼쪽으로 조정되므로 동일한 포인터를 해제 할 수 있습니다. memmove를 사용하지 않으므로 큰 문자열에는 적합하지 않습니다. @ jfm3이 언급 한 ++ /-연산자를 포함합니다. FCTX 기반 단위 테스트가 포함되었습니다.
#include <ctype.h>
void trim(char * const a)
{
char *p = a, *q = a;
while (isspace(*q)) ++q;
while (*q) *p++ = *q++;
*p = '\0';
while (p > a && isspace(*--p)) *p = '\0';
}
/* See http://fctx.wildbearsoftware.com/ */
#include "fct.h"
FCT_BGN()
{
FCT_QTEST_BGN(trim)
{
{ char s[] = ""; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = " "; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = "\t"; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = "a"; trim(s); fct_chk_eq_str("a", s); } // NOP
{ char s[] = "abc"; trim(s); fct_chk_eq_str("abc", s); } // NOP
{ char s[] = " a"; trim(s); fct_chk_eq_str("a", s); } // Leading
{ char s[] = " a c"; trim(s); fct_chk_eq_str("a c", s); } // Leading
{ char s[] = "a "; trim(s); fct_chk_eq_str("a", s); } // Trailing
{ char s[] = "a c "; trim(s); fct_chk_eq_str("a c", s); } // Trailing
{ char s[] = " a "; trim(s); fct_chk_eq_str("a", s); } // Both
{ char s[] = " a c "; trim(s); fct_chk_eq_str("a c", s); } // Both
// Villemoes pointed out an edge case that corrupted memory. Thank you.
// http://stackoverflow.com/questions/122616/#comment23332594_4505533
{
char s[] = "a "; // Buffer with whitespace before s + 2
trim(s + 2); // Trim " " containing only whitespace
fct_chk_eq_str("", s + 2); // Ensure correct result from the trim
fct_chk_eq_str("a ", s); // Ensure preceding buffer not mutated
}
// doukremt suggested I investigate this test case but
// did not indicate the specific behavior that was objectionable.
// http://stackoverflow.com/posts/comments/33571430
{
char s[] = " foobar"; // Shifted across whitespace
trim(s); // Trim
fct_chk_eq_str("foobar", s); // Leading string is correct
// Here is what the algorithm produces:
char r[16] = { 'f', 'o', 'o', 'b', 'a', 'r', '\0', ' ',
' ', 'f', 'o', 'o', 'b', 'a', 'r', '\0'};
fct_chk_eq_int(0, memcmp(s, r, sizeof(s)));
}
}
FCT_QTEST_END();
}
FCT_END();
하나는 실제 작업을 수행하는 다른 하나입니다.
#include <stdio.h>
int main()
{
const char *target = " haha ";
char buf[256];
sscanf(target, "%s", buf); // Trimming on both sides occurs here
printf("<%s>\n", buf);
}
%n
변환 지정자 를 사용하여 건너 뛴 문자에 대한 루프와 카운터가 필요하며 결국 수동으로 수행하는 것이 더 간단합니다.
나는 다음 중 하나 이상을 수행했기 때문에 이러한 답변을 좋아하지 않았습니다 ...
내 버전은 다음과 같습니다.
void fnStrTrimInPlace(char *szWrite) {
const char *szWriteOrig = szWrite;
char *szLastSpace = szWrite, *szRead = szWrite;
int bNotSpace;
// SHIFT STRING, STARTING AT FIRST NON-SPACE CHAR, LEFTMOST
while( *szRead != '\0' ) {
bNotSpace = !isspace((unsigned char)(*szRead));
if( (szWrite != szWriteOrig) || bNotSpace ) {
*szWrite = *szRead;
szWrite++;
// TRACK POINTER TO LAST NON-SPACE
if( bNotSpace )
szLastSpace = szWrite;
}
szRead++;
}
// TERMINATE AFTER LAST NON-SPACE (OR BEGINNING IF THERE WAS NO NON-SPACE)
*szLastSpace = '\0';
}
isspace
하는 unsigned char
, 그렇지 않으면 당신은 정의되지 않은 동작을 호출.
while (isspace((unsigned char) *szWrite)) szWrite++;
은 그것을 막을 것입니다. 코드는 또한 후행 공백을 모두 복사합니다.
*szWrite = *szRead
대해 포인터가 같지 않을 때만 수행 하면 쓰기를 건너 뛰지 만 다른 비교 / 분기를 추가했습니다. 최신 CPU / MMU / BP를 사용하면 해당 검사가 손실 또는 이득인지 알 수 없습니다. 더 간단한 프로세서와 메모리 아키텍처를 사용하면 복사를 수행하고 비교를 건너 뛰는 것이 더 저렴합니다.
파티에 늦었 어 ...
역 추적이없는 단일 패스 정방향 스캔 솔루션. 소스 문자열의 모든 문자는 정확히 한 번만 테스트됩니다 두 번. (따라서 소스 문자열에 후행 공백이 많은 경우 특히 다른 솔루션보다 빠릅니다.)
여기에는 소스 문자열을 다른 대상 문자열로 복사 및 트리밍하는 것과 다른 방법으로 소스 문자열을 트리밍하는 두 가지 솔루션이 포함됩니다. 두 함수 모두 동일한 코드를 사용합니다.
(수정 가능) 문자열은 제자리로 이동하므로 원래 포인터는 변경되지 않습니다.
#include <stddef.h>
#include <ctype.h>
char * trim2(char *d, const char *s)
{
// Sanity checks
if (s == NULL || d == NULL)
return NULL;
// Skip leading spaces
const unsigned char * p = (const unsigned char *)s;
while (isspace(*p))
p++;
// Copy the string
unsigned char * dst = (unsigned char *)d; // d and s can be the same
unsigned char * end = dst;
while (*p != '\0')
{
if (!isspace(*dst++ = *p++))
end = dst;
}
// Truncate trailing spaces
*end = '\0';
return d;
}
char * trim(char *s)
{
return trim2(s, s);
}
'\0'
테스트됩니다 isspace()
. 로 모든 문자를 테스트하는 것은 낭비적인 것 같습니다 isspace()
. 병리가 아닌 경우에는 문자열 끝에서 역 추적하는 것이 더 효율적입니다.
trim()
확인. 코너 케이스 : 와 겹치면 trim2(char *d, const char *s)
문제가 있습니다. d,s
s < d
trim()
행동 해야 합니까? 문자열 자체가 차지하는 메모리로 문자열을 자르고 복사하도록 요청하고 있습니다. 와 달리 memmove()
트림 자체를 수행하기 전에 소스 문자열의 길이를 결정해야하므로 전체 문자열을 추가로 스캔해야합니다. rtrim2()
소스를 대상에 뒤로 복사하는 것을 알고 다른 소스 문자열 길이 인수를 취하는 다른 함수 를 작성하는 것이 좋습니다.
"무통"이라고 생각하는 것이 확실하지 않습니다.
C 문자열은 꽤 고통 스럽습니다. 공백이 아닌 첫 번째 문자 위치를 간단하게 찾을 수 있습니다.
while (isspace (* p)) p ++;
우리는 두 개의 유사한 간단한 움직임으로 공백이 아닌 마지막 문자 위치를 찾을 수 있습니다.
while (* q) q ++; {q--; } while (isspace (* q));
( *
와 ++
연산자를 동시에 사용하는 데 따르는 고통을 아끼지 않았습니다 .)
문제는 지금 당신이 이것으로 무엇을합니까? 현재 데이터 유형은 String
생각하기 쉬운 큰 강력한 추상 이 아니라 실제로 스토리지 바이트 배열 이상입니다. 강력한 데이터 유형이 없기 때문에 PHperytonby의 chomp
기능 과 동일한 기능을 작성하는 것은 불가능 합니다. C에서 그러한 함수는 무엇을 반환합니까?
do { q--; } ...
알기 전에 한 번 확인해야 합니다 *q != 0
.
예를 들어 문자열 라이브러리를 사용하십시오 .
Ustr *s1 = USTR1(\7, " 12345 ");
ustr_sc_trim_cstr(&s1, " ");
assert(ustr_cmp_cstr_eq(s1, "12345"));
... 이것이 "일반적인"문제라고 말했듯이, 네는 #include 등을 포함해야하며 libc에는 포함되지 않지만 임의의 포인터를 저장하는 자체 해킹 작업과 size_t를 발명하지는 않습니다. 버퍼 오버 플로우.
당신이 사용하는 경우 glib
, 당신은 사용할 수 있습니다 g_strstrip
이 성장을 유지하기 위해 수정 가능한 문자열이있는 옵션이 하나 더 있습니다.
void trimString(char *string)
{
size_t i = 0, j = strlen(string);
while (j > 0 && isspace((unsigned char)string[j - 1])) string[--j] = '\0';
while (isspace((unsigned char)string[i])) i++;
if (i > 0) memmove(string, string + i, j - i + 1);
}
strlen()
size_t
범위를 초과 할 수있는를 반환합니다 int
. 공백은 공백 문자로 제한되지 않습니다. 마지막으로 가장 중요합니다. strcpy(string, string + i * sizeof(char));
소스 및 대상 배열이 겹치므로 정의되지 않은 동작입니다 . memmove()
대신에 사용하십시오 strcpy()
.
while (isspace((int)string[i])) string[i--] = '\0';
문자열의 시작 부분을 넘어서 반복 될 수 있음 을 언급하지 않았습니다 . 이 루프를 이전 및 다음 행과 결합하고 다음과 같이 작성해야합니다.while (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
end
후행 null 바이트를 가리 키지 않았으므로 end = ++i;
모든 공백 문자를 포함하는 문자열에 여전히 문제가있었습니다. 방금 코드를 수정했습니다.
나는 많은 답변이 있다는 것을 알고 있지만 내 해결책이 충분한 지 확인하기 위해 여기에 답변을 게시합니다.
// Trims leading whitespace chars in left `str`, then copy at almost `n - 1` chars
// into the `out` buffer in which copying might stop when the first '\0' occurs,
// and finally append '\0' to the position of the last non-trailing whitespace char.
// Reture the length the trimed string which '\0' is not count in like strlen().
size_t trim(char *out, size_t n, const char *str)
{
// do nothing
if(n == 0) return 0;
// ptr stop at the first non-leading space char
while(isspace(*str)) str++;
if(*str == '\0') {
out[0] = '\0';
return 0;
}
size_t i = 0;
// copy char to out until '\0' or i == n - 1
for(i = 0; i < n - 1 && *str != '\0'; i++){
out[i] = *str++;
}
// deal with the trailing space
while(isspace(out[--i]));
out[++i] = '\0';
return i;
}
isspace(*str)
UB when *str < 0
.
size_t n
것이 좋지만 n
완전한 트리밍 된 문자열에 비해 너무 작을 때 인터페이스는 어떤 식 으로든 호출자에게 알리지 않습니다 . 고려trim(out, 12, "delete data not")
문자열에서 선행 공백을 건너 뛰는 가장 쉬운 방법은 imho,
#include <stdio.h>
int main()
{
char *foo=" teststring ";
char *bar;
sscanf(foo,"%s",bar);
printf("String is >%s<\n",bar);
return 0;
}
" foo bar "
.
좋아, 이것이 나의 질문이다. 나는 그것이 제자리에서 문자열을 수정하고 ( free
작동 할) UB를 피하는 가장 간결한 솔루션이라고 생각합니다 . 작은 줄의 경우 아마도 memmove와 관련된 솔루션보다 빠릅니다.
void stripWS_LT(char *str)
{
char *a = str, *b = str;
while (isspace((unsigned char)*a)) a++;
while (*b = *a++) b++;
while (b > str && isspace((unsigned char)*--b)) *b = 0;
}
b > str
시험은 한 번만 필요합니다. *b = 0;
한 번만 필요했습니다.
#include <ctype.h>
#include <string.h>
char *trim_space(char *in)
{
char *out = NULL;
int len;
if (in) {
len = strlen(in);
while(len && isspace(in[len - 1])) --len;
while(len && *in && isspace(*in)) ++in, --len;
if (len) {
out = strndup(in, len);
}
}
return out;
}
isspace
모든 공백을 제거하는 데 도움이됩니다.
strndup
공백을 제외하여 새 문자열 버퍼를 만드는 데 사용 하십시오.strndup()
은 C 표준의 일부가 아니라 Posix입니다. 그러나 구현하기가 쉽기 때문에 큰 문제는 아닙니다.
trim_space("")
을 반환합니다 NULL
. 에 대한 포인터가 필요합니다 ""
. int len;
이어야합니다 size_t len;
. isspace(in[len - 1])
UB in[len - 1] < 0
.
while (isspace((unsigned char) *in) in++;
이전 len = strlen(in);
보다 초기 보다 더 효율적일 것입니다.while(len && *in && isspace(*in)) ++in, --len;
개인적으로, 나는 내 자신의 롤. strtok을 사용할 수 있지만 어떤 메모리가 무엇인지 알기 위해 (특히 선행 문자를 제거하는 경우)주의해야합니다.
후행 공백을 제거하는 것은 쉽고 안전합니다. 마지막 공간의 맨 위에 0을 넣고 끝에서 세어 볼 수 있습니다. 선행 공간을 없애는 것은 물건을 옮기는 것을 의미합니다. 적절한 장소에 배치하고 싶다면 (현실적으로) 선행 공백이 없을 때까지 모든 문자를 한 문자 뒤로 이동하면됩니다. 또는보다 효율적으로하기 위해 공백이 아닌 첫 번째 문자의 색인을 찾아 모든 숫자를 해당 숫자만큼 되돌릴 수 있습니다. 또는 공백이 아닌 첫 번째 문자에 대한 포인터를 사용할 수도 있습니다 (그러나 strtok에서와 같은 방식으로주의해야합니다).
#include "stdafx.h"
#include "malloc.h"
#include "string.h"
int main(int argc, char* argv[])
{
char *ptr = (char*)malloc(sizeof(char)*30);
strcpy(ptr," Hel lo wo rl d G eo rocks!!! by shahil sucks b i g tim e");
int i = 0, j = 0;
while(ptr[j]!='\0')
{
if(ptr[j] == ' ' )
{
j++;
ptr[i] = ptr[j];
}
else
{
i++;
j++;
ptr[i] = ptr[j];
}
}
printf("\noutput-%s\n",ptr);
return 0;
}
게임에 조금 늦었지만, 나는 내 일상을 싸움에 빠뜨릴 것이다. 아마도 가장 효율적이지 않을 수도 있지만 정확하고 간단하다고 생각합니다 ( rtrim()
복잡성 범위 를 밀고 있음).
#include <ctype.h>
#include <string.h>
/*
Public domain implementations of in-place string trim functions
Michael Burr
michael.burr@nth-element.com
2010
*/
char* ltrim(char* s)
{
char* newstart = s;
while (isspace( *newstart)) {
++newstart;
}
// newstart points to first non-whitespace char (which might be '\0')
memmove( s, newstart, strlen( newstart) + 1); // don't forget to move the '\0' terminator
return s;
}
char* rtrim( char* s)
{
char* end = s + strlen( s);
// find the last non-whitespace character
while ((end != s) && isspace( *(end-1))) {
--end;
}
// at this point either (end == s) and s is either empty or all whitespace
// so it needs to be made empty, or
// end points just past the last non-whitespace character (it might point
// at the '\0' terminator, in which case there's no problem writing
// another there).
*end = '\0';
return s;
}
char* trim( char* s)
{
return rtrim( ltrim( s));
}
char
하는 인수를 isspace()
할 (unsigned char)
가능성이 음의 값에 정의되지 않은 동작을 방지 할 수 있습니다. ltrim()
필요하지 않은 경우 줄을 움직이지 마십시오 .
지금까지 대부분의 답변은 다음 중 하나를 수행합니다.
strlen()
먼저 전화 를 걸어 전체 문자열을 두 번째 통과시킵니다.이 버전은 한 번만 패스하고 역 추적하지는 않습니다. 따라서 수백 개의 후행 공백 (일반적으로 SQL 쿼리의 출력을 처리 할 때 드문 일이 아님)이있는 경우에만 다른 것보다 성능이 향상 될 수 있습니다.
static char const WHITESPACE[] = " \t\n\r";
static void get_trim_bounds(char const *s,
char const **firstWord,
char const **trailingSpace)
{
char const *lastWord;
*firstWord = lastWord = s + strspn(s, WHITESPACE);
do
{
*trailingSpace = lastWord + strcspn(lastWord, WHITESPACE);
lastWord = *trailingSpace + strspn(*trailingSpace, WHITESPACE);
}
while (*lastWord != '\0');
}
char *copy_trim(char const *s)
{
char const *firstWord, *trailingSpace;
char *result;
size_t newLength;
get_trim_bounds(s, &firstWord, &trailingSpace);
newLength = trailingSpace - firstWord;
result = malloc(newLength + 1);
memcpy(result, firstWord, newLength);
result[newLength] = '\0';
return result;
}
void inplace_trim(char *s)
{
char const *firstWord, *trailingSpace;
size_t newLength;
get_trim_bounds(s, &firstWord, &trailingSpace);
newLength = trailingSpace - firstWord;
memmove(s, firstWord, newLength);
s[newLength] = '\0';
}
strspn()
및 strcspn()
꽉 루프있다. 이것은 매우 비효율적이며 오버 헤드는 단일 전달 패스의 입증되지 않은 이점을 능가합니다. strlen()
일반적으로 실제 문제가 아닌 매우 효율적인 코드로 인라인으로 확장됩니다. 흰색이 아닌 문자가 거의 없거나 전혀없는 특수한 문자열 인 경우에도 문자열의 시작과 끝을 트리밍하는 것이 문자열의 모든 문자를 테스트하여 백색도보다 훨씬 빠릅니다.
이것은 내가 생각할 수있는 가장 짧은 구현입니다.
static const char *WhiteSpace=" \n\r\t";
char* trim(char *t)
{
char *e=t+(t!=NULL?strlen(t):0); // *e initially points to end of string
if (t==NULL) return;
do --e; while (strchr(WhiteSpace, *e) && e>=t); // Find last char that is not \r\n\t
*(++e)=0; // Null-terminate
e=t+strspn (t,WhiteSpace); // Find first char that is not \t
return e>t?memmove(t,e,strlen(e)+1):t; // memmove string contents and terminator
}
char *trim(char *s) { char *p = s, *e = s + strlen(s); while (e > s && isspace((unsigned char)e[-1])) { *--e = '\0'; } while (isspace((unsigned char)*p)) { p++; } if (p > s) { memmove(s, p, e + 1 - p); } return s; }
이 함수는 원래 버퍼를 수정하므로 동적으로 할당 된 경우 원래 포인터를 해제 할 수 있습니다.
#include <string.h>
void rstrip(char *string)
{
int l;
if (!string)
return;
l = strlen(string) - 1;
while (isspace(string[l]) && l >= 0)
string[l--] = 0;
}
void lstrip(char *string)
{
int i, l;
if (!string)
return;
l = strlen(string);
while (isspace(string[(i = 0)]))
while(i++ < l)
string[i-1] = string[i];
}
void strip(char *string)
{
lstrip(string);
rstrip(string);
}
rstrip()
빈 문자열에서 정의되지 않은 동작을 호출합니다. lstrip()
공백 문자의 초기 부분이 길면 문자열에서 불필요하게 느립니다. 와 다른 음수 값에 대해 정의되지 않은 동작을 호출하기 때문에 인수에 isspace()
전달해서는 안됩니다 . char
EOF
Shlwapi.h 헤더에 정의 된 StrTrim 함수 사용에 대해 어떻게 생각하십니까? 그것은 스스로 정의하는 것이 간단합니다.
자세한 내용은
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx를 참조하십시오.
당신이있는 경우
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
이 줄 것이다 ausCaptain
로 "GeorgeBailey"
하지 "GeorgeBailey "
.
양쪽에서 문자열을 자르려면 oldie를 사용하지만 끈적 거리는 곳을 사용하십시오.) 공백보다 작은 ASCII로 자르는 것이 가능합니다. 즉 제어 문자도 자릅니다!
char *trimAll(char *strData)
{
unsigned int L = strlen(strData);
if(L > 0){ L--; }else{ return strData; }
size_t S = 0, E = L;
while((!(strData[S] > ' ') || !(strData[E] > ' ')) && (S >= 0) && (S <= L) && (E >= 0) && (E <= L))
{
if(strData[S] <= ' '){ S++; }
if(strData[E] <= ' '){ E--; }
}
if(S == 0 && E == L){ return strData; } // Nothing to be done
if((S >= 0) && (S <= L) && (E >= 0) && (E <= L)){
L = E - S + 1;
memmove(strData,&strData[S],L); strData[L] = '\0';
}else{ strData[0] = '\0'; }
return strData;
}
size_t
대신 사용해야 합니다 unsigned int
. 이 코드에는 많은 중복 테스트가 있으며 strncpy(strData,&strData[S],L)
소스 및 대상 배열이 겹치기 때문에 정의되지 않은 동작을 호출합니다 . memmove()
대신에 사용하십시오 strncpy()
.
지금까지 게시 된 코드가 차선책 인 것처럼 보이기 때문에 코드를 포함하고 있습니다 (아직 언급 할 담당자가 없습니다).
void inplace_trim(char* s)
{
int start, end = strlen(s);
for (start = 0; isspace(s[start]); ++start) {}
if (s[start]) {
while (end > 0 && isspace(s[end-1]))
--end;
memmove(s, &s[start], end - start);
}
s[end - start] = '\0';
}
char* copy_trim(const char* s)
{
int start, end;
for (start = 0; isspace(s[start]); ++start) {}
for (end = strlen(s); end > 0 && isspace(s[end-1]); --end) {}
return strndup(s + start, end - start);
}
strndup()
GNU 확장입니다. 당신이 그것 또는 동등한 것을 가지고 있지 않으면, 당신의 자신의 롤. 예를 들면 다음과 같습니다.
r = strdup(s + start);
r[end-start] = '\0';
isspace(0)
false로 정의 된 경우 두 기능을 모두 단순화 할 수 있습니다. 또한 블록 memmove()
내부를 움직 if
입니다.
여기에서는 동적 메모리 할당을 사용하여 입력 문자열을 trimStr 함수로 자릅니다. 먼저, 입력 문자열에 비어 있지 않은 문자가 몇 개 있는지 확인합니다. 그런 다음 해당 크기의 문자 배열을 할당하고 널 종료 문자를 처리합니다. 이 함수를 사용할 때는 메인 함수 내부의 메모리를 비워야합니다.
#include<stdio.h>
#include<stdlib.h>
char *trimStr(char *str){
char *tmp = str;
printf("input string %s\n",str);
int nc = 0;
while(*tmp!='\0'){
if (*tmp != ' '){
nc++;
}
tmp++;
}
printf("total nonempty characters are %d\n",nc);
char *trim = NULL;
trim = malloc(sizeof(char)*(nc+1));
if (trim == NULL) return NULL;
tmp = str;
int ne = 0;
while(*tmp!='\0'){
if (*tmp != ' '){
trim[ne] = *tmp;
ne++;
}
tmp++;
}
trim[nc] = '\0';
printf("trimmed string is %s\n",trim);
return trim;
}
int main(void){
char str[] = " s ta ck ove r fl o w ";
char *trim = trimStr(str);
if (trim != NULL )free(trim);
return 0;
}
내가하는 방법은 다음과 같습니다. 문자열을 제자리에 트림하므로 반환 된 문자열을 할당 해제하거나 할당 된 문자열에 대한 포인터를 잃을 염려가 없습니다. 가능한 가장 짧은 답변은 아니지만 대부분의 독자에게 명확해야합니다.
#include <ctype.h>
#include <string.h>
void trim_str(char *s)
{
const size_t s_len = strlen(s);
int i;
for (i = 0; i < s_len; i++)
{
if (!isspace( (unsigned char) s[i] )) break;
}
if (i == s_len)
{
// s is an empty string or contains only space characters
s[0] = '\0';
}
else
{
// s contains non-space characters
const char *non_space_beginning = s + i;
char *non_space_ending = s + s_len - 1;
while ( isspace( (unsigned char) *non_space_ending ) ) non_space_ending--;
size_t trimmed_s_len = non_space_ending - non_space_beginning + 1;
if (s != non_space_beginning)
{
// Non-space characters exist in the beginning of s
memmove(s, non_space_beginning, trimmed_s_len);
}
s[trimmed_s_len] = '\0';
}
}
char* strtrim(char* const str)
{
if (str != nullptr)
{
char const* begin{ str };
while (std::isspace(*begin))
{
++begin;
}
auto end{ begin };
auto scout{ begin };
while (*scout != '\0')
{
if (!std::isspace(*scout++))
{
end = scout;
}
}
auto /* std::ptrdiff_t */ const length{ end - begin };
if (begin != str)
{
std::memmove(str, begin, length);
}
str[length] = '\0';
}
return str;
}