printf 대신 선행 줄 바꿈을 사용하는 이유는 무엇입니까?


79

를 사용할 때 줄 바꿈을 피해야한다고 들었습니다 printf. 대신 있도록 printf("\nHello World!")사용한다printf("Hello World!\n")

위의이 특정 예에서는 출력이 다르기 때문에 의미가 없지만 다음을 고려하십시오.

printf("Initializing");
init();
printf("\nProcessing");
process_data();
printf("\nExiting");

다음과 비교 :

printf("Initializing\n");
init();
printf("Processing\n");
process_data();
printf("Exiting");

나는 줄 바꿈이 더 나아진다는 점을 제외하고는 줄 바꿈 후 어떤 이점도 볼 수 없습니다. 다른 이유가 있습니까?

편집하다:

나는 지금 그리고 가까운 투표를 다룰 것입니다. 이 질문은 주로 디자인에 관한 것이기 때문에 이것이 스택 오버플로에 속한다고 생각하지 않습니다. 또한이 문제에 대한 의견 일 수도 있지만 Kilian Foth의 답변cmaster의 답변 은 한 가지 접근 방식으로 실제로 객관적인 이점이 있음을 증명합니다.


5
이 질문은 "코드가있는 이슈"(주제 외)와 "개념적 소프트웨어 디자인"(주제) 사이의 경계에 있습니다. 닫힐 수 있지만 너무 세게 들지 마십시오. 그럼에도 불구하고 구체적인 코드 예제를 추가하는 것이 올바른 선택이라고 생각합니다.
Kilian Foth

46
마지막 줄은 후행 줄 바꿈없이 Linux에서 명령 프롬프트와 병합됩니다.
GrandmasterB

4
그것이 "더 좋아 보인다"고 단점이 없다면, 그것은 그것을 할 충분한 이유입니다, IMO. 좋은 코드를 작성하는 것은 좋은 소설이나 좋은 기술 논문을 작성하는 것과 다르지 않습니다. 악마는 항상 자세합니다.
alephzero

5
수행 init()process_data()무엇이든 자신을 인쇄? 결과가 어떤 모습 일지 기대하십니까?
Bergi

9
\n구분자 가 아니라 줄 종결 자 입니다. 이것은 UNIX에서 텍스트 파일이 거의 항상로 끝나는 사실에 의해 입증됩니다 . \n
Jonathon Reinhart

답변:


222

상당한 양의 터미널 I / O가 라인 버퍼링 되므로 \ n으로 메시지를 끝내면 적시에 표시 될 것입니다. 선행 \ n을 사용하면 메시지가 한 번에 표시되거나 표시되지 않을 수 있습니다. 종종 이것은 각 단계가 이전 단계 의 진행 메시지를 표시한다는 것을 의미하며 , 이는 프로그램의 동작을 이해하려고 할 때 혼란을 끝내지 않고 시간을 낭비합니다.


20
이것은 충돌하는 프로그램을 디버깅하기 위해 printf를 사용할 때 특히 중요합니다. printf의 끝에 개행 문자를 넣으면 콘솔의 stdout이 각 printf에서 플러시됩니다. (stdout이 파일로 경로 재 지정 될 때, std 라이브러리는 일반적으로 라인 버퍼링 대신 블록 버퍼링을 수행하므로, 마지막에 줄 바꿈이 있어도 printf 디버깅이 충돌을 매우 어렵게합니다.
Erik Eidt

25
@ErikEidt fprintf(STDERR, …)대신 진단 출력을 위해 버퍼링되지 않는 대신 사용해야 합니다.
중복 제거기

4
@Deduplicator 오류 스트림에 진단 메시지를 작성하면 단점도 있습니다. 많은 스크립트는 오류 스트림에 무언가가 쓰여지면 프로그램이 실패했다고 가정합니다.
Voo

54
@ Voo : stderr에 쓰는 것으로 가정하는 모든 프로그램이 실패 자체를 나타내는 것이라고 주장합니다. 프로세스의 종료 코드는 실패했는지 여부를 나타냅니다. 실패한 경우 stderr 출력에 이유 가 설명되어 있습니다. 프로세스가 성공적으로 종료 된 경우 (종료 코드 0) stderr 출력은 특정 기계 구문 분석 가능한 의미 체계 (예 : 사람이 읽을 수있는 경고를 포함 할 수 있음)가없는 인간 사용자에 대한 정보 출력으로 간주되어야하며 stdout은 실제 출력입니다. 추가 처리에 적합한 프로그램.
다니엘 Pryden

23
@Voo : 어떤 프로그램을 설명하고 있습니까? 나는 당신이 묘사하는 것처럼 행동하는 널리 사용되는 소프트웨어 패키지를 모른다. 나는 그러한 프로그램이 있다는 것을 알고 있지만, 내가 위에서 설명한 관습을 구성한 것과는 다릅니다. 그것이 유닉스 나 유닉스와 같은 환경에서 프로그램의 대부분이 작동하는 방식이며, 제 지식과 대부분의 프로그램에는 항상 있습니다. 나는 어떤 스크립트가 잘 처리하지 않기 때문에 stderr에 쓰는 것을 피하기 위해 어떤 프로그램도 옹호하지 않을 것입니다.
Daniel Pryden

73

POSIX 시스템 (기본적으로 모든 Linux, BSD, 오픈 소스 기반 시스템에서 찾을 수있는 모든 시스템)에서 줄은 개행 문자로 끝나는 문자열로 정의됩니다 \n. 이 모든 표준 명령 줄 도구 (이에 국한되지 않음)을 포함하여,시 건설 기본 가정이다 wc, grep, sed, awk,와 vim. 이는 또한 (와 같은 vim) 일부 편집기 \n가 파일 끝에 항상 a 를 추가하는 이유와 C의 이전 표준에서 \n문자 로 끝나는 헤더를 요구 한 이유이기도 합니다.

Btw : \n줄 을 종료하면 텍스트를 훨씬 쉽게 처리 할 수 ​​있습니다. 종료자가 있으면 완전한 줄을 갖게됩니다. 그리고 터미네이터를 아직 만나지 않았다면 더 많은 캐릭터를 봐야한다는 것을 확실히 알고 있습니다.

물론 이것은 프로그램의 입력 쪽이지만 프로그램 출력은 종종 프로그램 입력으로 다시 사용됩니다. 따라서 출력은 다른 프로그램에 원활하게 입력 할 수 있도록하기위한 규칙을 준수해야합니다.


25
이는 소프트웨어 공학에서 가장 오래된 논쟁 중 하나입니다 : 그것은 라인으로 (프로그래밍 언어, 세미콜론 같은 다른 "마지막 문장의"마커, 또는) 줄 바꿈을 사용하는 것이 좋습니다 터미네이터 또는 라인 분리기 ? 두 방법 모두 장단점이 있습니다. Windows 세계는 대부분 개행 시퀀스 (일반적으로 CR LF 임)가 줄 구분 기호 이므로 파일의 마지막 줄이 끝날 필요가 없다는 생각에 주로 정착 했습니다. 그러나 유닉스 세계에서 줄 바꿈 시퀀스 (LF)는 줄 종결 자 이며이 가정을 중심으로 많은 프로그램이 구축됩니다.
Daniel Pryden

33
POSIX 는 라인을 "0 개 이상의 비 개행 문자 와 종료 개행 문자 의 시퀀스"정의 합니다 .
파이프

6
@pipe가 말했듯이 POSIX 사양에 있다고 가정 하면 대답이 제안하는 것과는 달리 사실상의 법칙 이라고 부를 수 있습니까?
Baldrickk

4
@Baldrickk 맞아. 나는 지금 더 긍정적으로 답변을 업데이트했습니다.
cmaster

C는 또한 소스 파일에 대해 이러한 규칙을 만듭니다. 줄 바꿈으로 끝나지 않은 비어 있지 않은 소스 파일은 정의되지 않은 동작을 생성합니다.
R ..

31

다른 사람들이 언급 한 것 외에도 훨씬 간단한 이유가 있다고 느낍니다. 그것이 표준입니다. STDOUT으로 인쇄 할 때마다 거의 항상 새로운 줄 있다고 가정 하므로 새로운 줄을 시작할 필요가 없습니다. 또한 작성 될 다음 행이 같은 방식으로 작동한다고 가정하므로 새 행을 시작하면 도움이됩니다.

표준 후행 줄 바꿈 줄이 삽입 된 선행 줄 바꿈을 출력하면 다음과 같이 표시됩니다.

Trailing-newline-line
Trailing-newline-line

Leading-newline-line
Leading-newline-line
Leading-newline-lineTrailing-newline-line
Trailing-newline-line

Leading-newline-lineTrailing-newline-line
Trailing-newline-line
Trailing-newline-line

... 아마도 당신이 원하는 것이 아닙니다.

코드에서 선행 줄 바꿈 만 사용하고 IDE에서만 실행하면 괜찮습니다. 터미널에서 실행하거나 코드와 함께 STDOUT에 쓸 다른 사람들의 코드를 도입하자마자 위와 같이 바람직하지 않은 출력이 표시됩니다.


2
대화식 쉘에서 중단 된 프로그램에서도 비슷한 현상이 발생합니다. 부분 행이 인쇄되면 (줄 바꿈이 누락 된 경우) 커서가있는 열에 대해 쉘이 혼동되어 다음 명령 행을 편집하기 어렵습니다. 에 새로운 줄 바꿈을 추가하지 않으면 $PS1기존 프로그램에 따라 자극을 줄 수 있습니다 .
Toby Speight

17

고도로 찬성 된 답변은 이미 후행 줄 바꿈이 선호되어야하는 훌륭한 기술적 이유를 제시 했으므로 다른 각도에서 접근 할 것입니다.

내 의견으로는, 다음은 프로그램을 더 읽기 쉽게 만든다 :

  1. 높은 신호대 잡음비 (일명 단순하지만 단순하지는 않음)
  2. 중요한 아이디어가 먼저 나온다

위의 관점에서, 우리는 후행 줄 바꿈이 더 낫다고 주장 할 수 있습니다. 줄 바꿈은 메시지와 비교할 때 "노이즈"형식을 지정하므로 메시지가 눈에 띄어 야하므로 먼저 구문을 강조해야합니다 (구문 강조 표시도 도움이 될 수 있음).


19
예, ... "ok\n"보다 훨씬 낫습니다"\nok"
cmaster

@ cmaster : C에서 Pascal-string API를 사용하여 MacOS에 대해 읽은 것을 상기시켜줍니다.이 문자열에는 모든 문자열 리터럴 앞에와 같은 마술 이스케이프 코드가 필요했습니다 "\pFoobar".
grawity

16

후행 줄 바꿈을 사용하면 나중에 수정하는 것이 간단 해집니다.

OP의 코드를 기반으로 한 (매우 간단한) 예제로서 , "초기화"메시지 전에 일부 출력을 생성해야 하고 해당 출력이 다른 소스 파일의 코드의 다른 논리적 부분에서 나온다고 가정하십시오.

첫 번째 테스트를 실행하고 "초기화"가 다른 출력의 줄 끝에 추가되면 코드를 검색하여 인쇄 된 위치를 찾은 다음 "초기화"를 "\ n 초기화"로 변경해야합니다. "은 다른 상황에서 다른 형식을 망치지 않습니다.

이제 새 출력이 실제로 선택 사항이라는 사실을 처리하는 방법을 고려하여 "\ nInitializing"으로 변경하면 출력 시작시 원하지 않는 빈 줄이 생성되는 경우가 있습니다 ...

선행 출력이 있는지 여부를 나타내는 전역 ( 충격 공포 ?? !!! ) 플래그 를 설정하고 선택적인 선행 "\ n"으로 "초기화"를 인쇄하도록 테스트하거나 "\ n"을 함께 출력합니까? 이전의 결과물과 미래의 코드 리더에게이 "초기화" 에 다른 모든 출력 메시지와 같이 "\ n" 이없는 이유가 무엇인지 궁금해 합니까?

후행 줄 바꿈을 일관되게 출력하면 종료 해야하는 줄의 끝에 도달했다는 것을 알게 된 시점에서 모든 문제를 회피합니다. 행 단위로 출력하는 일부 논리의 끝에 별도의 puts ( "\ n") 문이 필요할 수 있지만 요점은 코드의 가장 빠른 위치에서 줄 바꿈을 출력하는 것입니다. 다른 곳이 아니라 그것을하십시오 .


1
모든 독립적 인 출력 항목이 자체 줄에 표시되어야하는 경우, 새 줄 뒤에서 정상적으로 작동 할 수 있습니다. 그러나 실용적으로 여러 항목을 통합해야하는 경우 상황이 더욱 복잡해집니다. 공통 루틴을 통해 모든 출력을 공급하는 것이 실용적이라면, 마지막 문자가 CR 인 경우 명확한 라인 끝을 삽입하는 조작, 마지막 문자가 개행 인 경우 아무것도, 마지막 문자 인 경우 개행 프로그램이 일련의 독립 라인을 출력하는 것 이외의 작업을 수행 해야하는 경우 도움이 될 수 있습니다.
supercat

7

printf 대신 선행 줄 바꿈을 사용하는 이유는 무엇입니까?

C 사양과 밀접하게 일치합니다.

C 라이브러리는 정의 라인 으로 종료개행 문자를 '\n' .

텍스트 스트림으로 이루어지는 문자들의 순서화 된 시퀀스 인 라인 0 개 이상의 문자로 구성된 각 라인 플러스 착신 개행 문자. 마지막 줄에 종결 개행 문자가 필요한지 여부는 구현 정의됩니다. C11 §7.21.2 2

데이터를 행으로 작성하는 코드 는 해당 라이브러리 개념과 일치합니다.

printf("Initializing"); // Write part of a line
printf("\nProcessing"); // Finish prior line & write part of a line
printf("\nExiting");    // Finish prior line & write an implementation-defined last line

printf("Initializing\n");//Write a line 
printf("Processing\n");  //Write a line
printf("Exiting");       //Write an implementation-defined last line

다시 : 마지막 줄에는 종료 줄 바꿈 문자가 필요합니다 . 나는 항상 '\n'출력에 결승 을 작성하고 입력에 결석을 허용 하는 것이 좋습니다 .


맞춤법 검사

내 맞춤법 검사기가 불평합니다. 아마도 당신도 마찬가지입니다.

  v---------v Not a valid word
"\nProcessing"

 v--------v OK
"Processing\n");

나는 한 번 ispell.el더 그것에 대처하기 위해 개선했다. \t문제 가 더 자주 발생 했음을 인정 하고 문자열을 여러 토큰으로 나누면 간단히 피할 수 있지만 HTML의 텍스트가 아닌 부분을 선택적으로 건너 뛰는 것이 더 일반적인 "무시"작업의 부작용 일뿐입니다. 또는 MIME 멀티 파트 본문 및 주석이 아닌 코드 부분. 필자는 항상 적절한 메타 데이터 (예 : <p lang="de_AT">또는 Content-Language: gd) 가있는 언어로 전환하는 것을 의미 했지만 결코 라운드 Tuit을 얻지 못했습니다. 그리고 관리자는 패치를 완전히 거부했습니다. :-(
Toby Speight

@TobySpeight 여기에 둥근 toit이 있습니다. 다시 개선해 보시기 바랍니다 ispell.el.
chux

4

새로운 줄 바꿈을 사용하면 조건부 (예 :

printf("Initializing");
if (jobName != null)
    printf(": %s", jobName);
init();
printf("\nProcessing");

(그러나 다른 곳에서 언급했듯이 많은 CPU 시간이 걸리는 단계를 수행하기 전에 출력 버퍼를 플러시해야 할 수도 있습니다.)

따라서 두 가지 방법 모두 좋은 사례를 만들 수는 있지만 개인적으로 printf ()를 좋아하지 않으며 출력을 빌드하기 위해 사용자 정의 클래스를 사용합니다.


1
후행 줄 바꿈이있는 버전보다이 버전을 작성하기 쉬운 이유를 설명해 주시겠습니까? 이 예에서는 나에게 분명하지 않습니다. 대신 다음 출력이와 같은 줄에 추가되어 발생하는 문제를 볼 수 있습니다 "\nProcessing".
Raimund Krämer

Raimund와 마찬가지로 이와 같은 작업으로 인해 발생하는 문제도 볼 수 있습니다. 전화 할 때는 주변 인쇄물을 고려해야합니다 printf. 전체 "초기화"라인을 조건화하려면 어떻게해야합니까? 줄 바꿈을 접두어로 사용해야하는지 여부를 확인하려면 해당 조건에 "프로세스"행을 포함시켜야합니다. 미리 다른 인쇄가 있고 "처리 중"줄을 조건부로 작성해야하는 경우 각 인쇄마다 다른 개행을 접두사로 붙여야하는지 알기 위해 해당 조건에 다음 인쇄를 포함해야합니다.
JoL

2
나는 그 원칙에 동의하지만 그 예는 좋지 않다. 보다 적절한 예는 한 줄에 몇 개의 항목을 출력해야하는 코드입니다. 출력이 개행으로 끝나는 헤더로 시작해야하고 각 행이 헤더로 시작해야하는 경우, 예를 들어 if ((addr & 0x0F)==0) printf("\n%08X:", addr);무조건 무조건 출력에 개행을 출력에 추가하는 것이 사용하는 것보다 쉽습니다 각 줄의 헤더와 후행 줄 바꿈에 대한 별도의 코드.
supercat

1

선도 뉴 라인은 다른 라이브러리 함수, 특히 잘 작동하지 않습니다 puts()perror표준 라이브러리뿐만 아니라 사용 가능성이있어 다른 라이브러리.

미리 작성된 줄을 인쇄하려면 (상수 또는 이미 형식이 지정된 줄) (예 :로 sprintf()), puts()자연스럽고 효율적인 선택입니다. 그러나 puts()이전 줄을 끝내고 끝나지 않은 줄을 쓸 수있는 방법은 없습니다 . 항상 줄 종결자를 씁니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.