K & R 연습 : 내 코드는 작동하지만 냄새가 난다. 정리에 대한 조언?

StackOverflow https://stackoverflow.com/questions/161873

  •  03-07-2019
  •  | 
  •  

문제

나는 K & R 책에서 일하고 있습니다. 나는 주로 시간이 부족한 운동보다 더 멀리 읽었습니다. 나는 따라 잡고 있으며, 튜토리얼 인 1 장에서 거의 모든 연습을 해왔습니다.

내 문제는 운동 1-18이었다. 운동은 다음과 같습니다.

입력 라인에서 후행 공백과 탭을 제거하고 완전히 빈 줄을 삭제하는 프로그램을 작성하십시오.

내 코드 (아래)가 그렇게하고 작동합니다. 그것에 대한 내 문제는 내가 구현 한 트림 방법입니다. 느낌 ... 잘못 ... 어떻게 든. 코드 검토에서 C#에서 비슷한 코드를 보았을 때 아마도 아마도 너트가 될 것입니다. (C# 내 전문 분야 중 하나입니다.)

누구나이를 청소하는 것에 대한 조언을 제공 할 수 있습니까? 단지 1 장과 기본 stdio.h에 대해 이야기합니다.) 또한 조언을 할 때 왜 도움이 될지 설명 할 수 있습니까? (결국, 배우려고 노력하고 있습니다! 그리고 여기 전문가들보다 더 잘 배우는 것이 더 나은가?)

#include <stdio.h>

#define MAXLINE 1000

int getline(char line[], int max);
void trim(char line[], char ret[]);

int main()
{
    char line[MAXLINE];
    char out[MAXLINE];
    int length;

    while ((length = getline(line, MAXLINE)) > 0)
    {
        trim(line, out);
        printf("%s", out);
    }

    return 0;
}

int getline(char line[], int max)
{
    int c, i;

    for (i = 0; i < max - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
        line[i] = c;

    if (c == '\n')
    {
        line[i] = c;
        ++i;
    }

    line[i] = '\0'; 
    return i;
}

void trim(char line[], char ret[])
{
    int i = 0;

    while ((ret[i] = line[i]) != '\0')
        ++i;

    if (i == 1)
    {
        // Special case to remove entirely blank line
        ret[0] = '\0';
        return;
    }

    for (  ; i >= 0; --i)
    {
        if (ret[i] == ' ' || ret[i] == '\t')
            ret[i] = '\0';
        else if (ret[i] != '\0' && ret[i] != '\r' && ret[i] != '\n')
            break;
    }

    for (i = 0; i < MAXLINE; ++i)
    {
        if (ret[i] == '\n')
        {
            break;
        }
        else if (ret[i] == '\0')
        {
            ret[i] = '\n';
            ret[i + 1] = '\0';
            break;
        }
    }
}

편집 : 여기에서보고있는 유용한 팁에 감사드립니다. 나는 사람들에게 내가 여전히 C를 가진 N00B이고 구체적으로 포인터를 얻지 못한다는 것을 사람들에게 상기시키고 싶습니다. (k & r -ch.1의 ch.1에 대한 비트를 기억하십시오.

그리고 내가 찾고있는 대부분의 것은 트림 방법 자체입니다. 특히 내가 반복하고 있다는 사실 3 시간 (너무 더러운 느낌). 나는 단지 더 영리한 느낌이 든다면 (C에 대한 고급 지식이 없더라도) 이것은 더 깨끗했을 수 있습니다.

도움이 되었습니까?

해결책

두 개의 버퍼가있을 이유가 없으며 입력 라인을 제자리에 트리밍 할 수 있습니다.

int trim(char line[])
{
    int len = 0;
    for (len = 0; line[len] != 0; ++len)
        ;

    while (len > 0 &&
           line[len-1] == ' ' && line[len-1] == '\t' && line[len-1] == '\n')
        line[--len] = 0;

    return len;
}

라인 길이를 반환하면 0이 아닌 길이 라인을 테스트하여 빈 줄을 제거 할 수 있습니다.

if (trim(line) != 0)
    printf("%s\n", line);

편집 : ASCII 인코딩을 가정 할 때 while 루프를 더 간단하게 만들 수 있습니다.

while (len > 0 && line[len-1] <= ' ')
    line[--len] = 0;

다른 팁

당신이 1 장을 고수하고 있다면, 그것은 나에게 꽤 좋아 보인다. 코드 검토 관점에서 추천 할 내용은 다음과 같습니다.

C에서 평등을 확인할 때 항상 상수를 먼저 두십시오.

if (1 == myvar)

그렇게하면 실수로 다음과 같은 일을하지 않습니다.

if (myvar = 1)

당신은 C#에서 그것을 벗어날 수는 없지만 C에서 잘 컴파일하며 디버그하는 진정한 악마가 될 수 있습니다.

trim ()가 너무 큽니다.

내가 필요하다고 생각하는 것은 strlen-ish 함수입니다 (계속해서 int stringlength (const char *s)를 작성).

그런 다음 시작시 시작시 시작되는 int scanback (const char *s, const char *matches, int start)이라는 함수가 필요합니다. 경기가 발견됩니다.

그런 다음 int scanfront (const char *s, const char *matches)라는 함수가 필요하고 0에서 시작하여 S에서 스캔되는 캐릭터가 일치에 포함되어있는 한 앞으로 스캔하여 일치가 발견되는 마지막 인덱스를 반환합니다.

그런 다음 int charinstring (char c, const char *s)이라는 함수가 필요하며 C가 s에 포함 된 경우 0이 아닌 경우, 그렇지 않으면 0입니다.

이것들과 관련하여 트림을 쓸 수 있어야합니다.

구성 중에 개인적으로 :

다음을 선호합니다.

while( (ret[i] = line[i]) )
        i++;

에게:

while ((ret[i] = line[i]) != '\0')
        ++i;

둘 다! = 0에 대해 확인하지만 첫 번째는 조금 더 깨끗해 보입니다. 숯이 다른 thah 0이라면 루프 본체가 다른 실행됩니다. 루프에서 벗어날 것입니다.

또한 'for'명령문의 경우, 성적으로 유효한 반면 다음과 같은 것을 알 수 있습니다.

for (  ; i >= 0; --i)

나에게 '이상한'것처럼 보이며 실제로는 잠재적 인 버그에 대한 잠재적 인 악몽 솔루션입니다. 이 코드를 검토하고 있다면 빛나는 빨간 경고와 같은 것 같습니다. 일반적으로 알려진 횟수를 반복하기 위해 루프에 사용하려고합니다. (항상 규칙에는 예외가 있지만 Ive는 일반적으로 사실이 사실임을 발견했습니다). 진술의 위의 내용은 다음과 같습니다.

while (i)
{
        if (ret[i] == ' ' || ret[i] == '\t')
        {
            ret[i--] = '\0';
        }
        else if (ret[i] != '\0' && ret[i] != '\r' && ret[i] != '\n')
        {
            break;
        }
}

가장 먼저:

int main (void)

main ()의 매개 변수를 알고 있습니다. 그들은 아무것도 아닙니다. (또는 Argc & Argv, 그러나 나는 그것이 1 장 자료라고 생각하지 않습니다.)

StyleWise, K & R 스타일 브래킷을 시도 할 수 있습니다. 수직 공간에서 훨씬 쉽습니다.

void trim(char line[], char ret[])
{
    int i = 0;

    while ((ret[i] = line[i]) != '\0')
        ++i;

    if (i == 1) { // Special case to remove entirely blank line
        ret[0] = '\0';
        return;
    }

    for (; i>=0; --i) { //continue backwards from the end of the line
        if ((ret[i] == ' ') || (ret[i] == '\t')) //remove trailing whitespace
            ret[i] = '\0';

        else if ((ret[i] != '\0') && (ret[i] != '\r') && (ret[i] != '\n')) //...until we hit a word character
            break;
    }

    for (i=0; i<MAXLINE-1; ++i) { //-1 because we might need to add a character to the line
        if (ret[i] == '\n') //break on newline
            break;

        if (ret[i] == '\0') { //line doesn't have a \n -- add it
            ret[i] = '\n';
            ret[i+1] = '\0';
            break;
        }
    }
}

(또한 주석을 추가하고 하나의 버그를 수정했습니다.)

큰 문제는 Maxline Constant의 사용법입니다. Main ()은 독점적으로 사용합니다. 그리고 밖으로 변수; 그들에게만 작동하는 Trim ()은 상수를 사용할 필요가 없습니다. getline ()에서와 마찬가지로 크기를 매개 변수로 전달해야합니다.

개인적으로 나는 다음과 같은 코드를 넣었습니다.

ret[i] != '\0' && ret[i] != '\r' && ret[i] != '\n'

별도의 함수 (또는 심지어 매크로 정의)로

  1. 트림은 실제로 1 개의 버퍼 만 사용해야합니다 (@ferruccio가 말한 것처럼).
  2. @plinth가 말한 것처럼 트림을 분해해야합니다.
  3. 트림 필요는 값을 반환 할 필요가 없습니다 (빈 문자열을 확인하려면 테스트 라인 [0] == 0)
  4. 추가 C 맛의 경우 인덱스 대신 포인터를 사용하십시오.

-라인 끝까지 가야합니다 (종료 0; -선의 시작 부분에 있지 않은 경우 현재 문자는 공간이지만 0으로 교체하십시오.

char *findEndOfString(char *string) {
  while (*string) ++string;
  return string; // string is now pointing to the terminating 0
}

void trim(char *line) {
  char *end = findEndOfString(line);
   // note that we start at the first real character, not at terminating 0
  for (end = end-1; end >= line; end--) {
      if (isWhitespace(*end)) *end = 0;
      else return;
  }
}

같은 일을하는 또 다른 예. C99 관련 제품을 사용하여 약간의 위반을했습니다. K & R에서는 찾을 수 없습니다. 또한 Starndard 라이브러리의 일부인 assert () 함수를 사용했지만 아마도 K & R의 장에서는 다루지 않을 것입니다.

#include <stdbool.h> /* needed when using bool, false and true. C99 specific. */
#include <assert.h> /* needed for calling assert() */

typedef enum {
  TAB = '\t',
  BLANK = ' '
} WhiteSpace_e;

typedef enum {
  ENDOFLINE = '\n',
  ENDOFSTRING = '\0'
} EndofLine_e;

bool isWhiteSpace(
  char character
) {
  if ( (BLANK == character) || (TAB == character ) ) {
    return true;
  } else {
    return false;
  }
}

bool isEndOfLine( 
  char character
) {
 if ( (ENDOFLINE == character) || (ENDOFSTRING == character ) ) {
    return true;
  } else {
    return false;
  }
}   

/* remove blanks and tabs (i.e. whitespace) from line-string */
void removeWhiteSpace(
  char string[]
) {
  int i;
  int indexOutput;

  /* copy all non-whitespace character in sequential order from the first to the last.
    whitespace characters are not copied */
  i = 0;
  indexOutput = 0;
  while ( false == isEndOfLine( string[i] ) ) {
    if ( false == isWhiteSpace( string[i] ) ) {
      assert ( indexOutput <= i );
      string[ indexOutput ] = string[ i ];
      indexOutput++;
    }
    i++; /* proceed to next character in the input string */
  }

  assert( isEndOfLine( string[ i ] ) );
  string[ indexOutput ] = ENDOFSTRING;

}

1 장 또는 K & R에 무엇이 있는지 모르고 운동에 대한 내 찌르레는 포인터를 가정합니까?

#include "stdio.h"

size_t StrLen(const char* s)
{
    // this will crash if you pass NULL
    size_t l = 0;
    const char* p = s;
    while(*p)
    {
        l++;
        ++p;
    }
    return l;
}

const char* Trim(char* s)
{
    size_t l = StrLen(s);
    if(l < 1)
        return 0;

    char* end = s + l -1;
    while(s < end && (*end == ' ' || *end == '\t'))
    {
        *end = 0;
        --end;
    }

    return s;
}

int Getline(char* out, size_t max)
{
    size_t l = 0;
    char c;
    while(c = getchar())
    {
        ++l;

        if(c == EOF) return 0;
        if(c == '\n') break;

        if(l < max-1)
        {
            out[l-1] = c;
            out[l] = 0;
        }
    }

    return l;
}

#define MAXLINE 1024

int main (int argc, char * const argv[]) 
{
    char line[MAXLINE];
    while (Getline(line, MAXLINE) > 0)
    {
        const char* trimmed = Trim(line);
        if(trimmed)
            printf("|%s|\n", trimmed);

        line[0] = 0;
    }

    return 0;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top