การลบอักขระบรรทัดใหม่ต่อท้ายจากอินพุต fgets ()


236

ฉันพยายามรับข้อมูลจากผู้ใช้และส่งไปยังฟังก์ชันอื่นในหน่วย gcc รหัสคืออะไรเช่นนี้

printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
    fprintf(stderr, "Error reading Name.\n");
    exit(1);
}

อย่างไรก็ตามฉันพบว่ามันมี\nตัวละครขึ้นบรรทัดใหม่ในตอนท้าย ดังนั้นถ้าฉันใส่มันจบลงด้วยการส่งJohn John\nฉันจะลบ\nและส่งสตริงที่เหมาะสมได้อย่างไร


21
if (!fgets(Name, sizeof Name, stdin))(อย่างน้อยอย่าใช้การปฏิเสธสองแบบ! และ! =)

4
@Roger กบาล "ไม่ใช้สอง negations" -> อืมถ้าเราขุดลึก "ทำไม่ได้" และ "ปฏิเสธ" มีทั้งnegations ;-) อาจเป็น "Use if (fgets(Name, sizeof Name, stdin)) {.
chux - Reinstate Monica

3
@chux ฉันแน่ใจว่าคุณหมายถึงif (fgets(Name, sizeof Name, stdin) == NULL ) {
R Sahu

@RSahu จริง : น่ารำคาญ!:
chux - Reinstate Monica

คำตอบ:


155

วิธีที่น่าเกลียดเล็กน้อย:

char *pos;
if ((pos=strchr(Name, '\n')) != NULL)
    *pos = '\0';
else
    /* input too long for buffer, flag error */

วิธีแปลก ๆ เล็กน้อย:

strtok(Name, "\n");

โปรดทราบว่าstrtokฟังก์ชั่นไม่ทำงานตามที่คาดไว้หากผู้ใช้ป้อนสตริงว่าง (เช่นกด Enter เท่านั้น) มันทำให้\nตัวละครเหมือนเดิม

แน่นอนว่ามีคนอื่นเช่นกัน


7
ไลบรารีรันไทม์ C ใด ๆ ที่รับรู้เธรด (ซึ่งส่วนใหญ่ส่วนใหญ่ที่กำหนดเป้าหมายเป็นแพลตฟอร์มแบบมัลติเธรด) strtok()จะเป็นเธรดที่ปลอดภัย (จะใช้ที่เก็บโลคัลของเธรดสำหรับสถานะ ที่กล่าวว่ามันยังคงดีกว่าการใช้strtok_r()ตัวแปรที่ไม่ได้มาตรฐาน (แต่ธรรมดาพอ)
Michael Burr

2
ดูคำตอบของฉันสำหรับตัวแปรที่ปลอดภัยสำหรับเธรดและ reentrant คล้ายกับstrtokแนวทางของคุณ(และใช้ได้กับอินพุตว่าง) ในความเป็นจริงเป็นวิธีที่ดีในการดำเนินการstrtokคือการใช้และstrcspn strspn
ทิม Febas

2
เป็นเรื่องสำคัญที่จะต้องจัดการกับกรณีอื่นหากคุณอยู่ในสภาพแวดล้อมที่มีความเสี่ยงต่อสายยาวเกิน อินพุตที่ถูกตัดอย่างเงียบ ๆ อาจทำให้เกิดข้อบกพร่องที่สร้างความเสียหายได้
Malcolm McLean

2
ถ้าคุณชอบหนึ่งสมุทรและใช้ glibc *strchrnul(Name, '\n') = '\0';ลอง
twobit

เมื่อstrchr(Name, '\n') == NULLนอกเหนือจาก "อินพุตนานเกินไปสำหรับบัฟเฟอร์ข้อผิดพลาดการตั้งค่าสถานะ" มีความเป็นไปได้อื่น ๆ : มีข้อความสุดท้ายที่stdinไม่ได้ลงท้ายด้วย'\n'อักขระโมฆะที่ฝังอยู่หรือหายาก
chux - Reinstate Monica

440

บางทีอาจจะเป็นทางออกที่ง่ายที่สุดใช้ฟังก์ชั่นหนึ่งที่รู้จักกันน้อยที่ชื่นชอบstrcspn():

buffer[strcspn(buffer, "\n")] = 0;

ถ้าคุณต้องการให้มันจัดการด้วย'\r'(เช่นถ้าสตรีมเป็นไบนารี่):

buffer[strcspn(buffer, "\r\n")] = 0; // works for LF, CR, CRLF, LFCR, ...

ฟังก์ชั่นนับจำนวนตัวอักษรจนกว่าจะถึง'\r'หรือ'\n'(ในคำอื่น ๆ ก็พบว่าตัวแรก'\r'หรือ'\n') ถ้ามันไม่กระทบอะไรมันก็หยุดที่'\0'(คืนความยาวของสตริง)

หมายเหตุว่านี้ทำงานได้ดีแม้ว่าจะไม่มีการขึ้นบรรทัดใหม่เพราะหยุดที่strcspn '\0'ในกรณีที่สายทั้งหมดเป็นเพียงการแทนที่ด้วย'\0''\0'


30
แม้จะจัดการกับสิ่งที่หายากbufferกว่าเริ่มต้นด้วย'\0'บางสิ่งที่ทำให้เกิดความเศร้าสลดสำหรับbuffer[strlen(buffer) - 1] = '\0';แนวทาง
chux - Reinstate Monica

5
@chux: ใช่แล้วฉันหวังว่าจะมีคนรู้จักstrcspn()อีกมาก หนึ่งในฟังก์ชันที่มีประโยชน์มากขึ้นในไลบรารี IMO ฉันได้ตัดสินใจที่จะเขียนและเผยแพร่แฮ็ค C ทั่วไปแบบนี้วันนี้; strtok_rการดำเนินการใช้strcspnและstrspnเป็นหนึ่งในคนแรก: codepad.org/2lBkZk0w ( คำเตือน:ฉันไม่สามารถรับประกันได้ว่ามันไม่โรคจิตมันถูกเขียนขึ้นอย่างเร่งรีบและอาจจะมีไม่กี่) ฉันไม่รู้ว่าฉันจะเผยแพร่ยังไง แต่ฉันตั้งใจจะทำมันด้วยจิตวิญญาณของ "hacks twiddling บิตที่มีชื่อเสียง"
ทิม Febas

4
ดูวิธีการตัดแต่งอย่างมี ประสิทธิภาพ นี่น่าจะเป็นหนึ่งซับที่ถูกต้อง เท่านั้น เร็วกว่า - แม้ว่าจะไม่ง่าย fgets()strcspn()strlen
chux - Reinstate Monica

6
@sidbushes: คำถามทั้งในชื่อเรื่องและเนื้อหาที่ถามเกี่ยวกับการขึ้นบรรทัดใหม่ต่อท้ายจากfgets()การป้อนข้อมูล ซึ่งยังขึ้นบรรทัดแรกเสมอ
ทิม Apras

9
@sidbushes: ฉันเข้าใจว่าคุณมาจากไหน แต่ฉันไม่สามารถรับผิดชอบต่อผลการค้นหาของ Google สำหรับคำที่เฉพาะเจาะจง คุยกับ Google ไม่ใช่ฉัน
Tim Čas

83
size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n') 
    name[ln] = '\0';

8
อาจจะมีข้อยกเว้นถ้าสตริงว่างเปล่าใช่ไหม? ชอบดัชนีอยู่นอกช่วง
Edward Olamisan

1
@EdwardOlamisan สตริงจะไม่ว่างเปล่า
James Morris

5
@James มอร์ริสในกรณีที่ผิดปกติ->fgets(buf, size, ....) strlen(buf) == 01) fgets()อ่านเป็นครั้งแรก 2) 3) ผลตอบแทนจากนั้นเนื้อหาอาจเป็นอะไรก็ได้ (รหัสของ OP ทำการทดสอบค่า NULL) แนะนำ:char'\0'size == 1fgets()NULLbufsize_t ln = strlen(name); if (ln > 0 && name[ln-1] == '\n') name[--ln] = '\0';
chux - Reinstate Monica

2
เกิดอะไรขึ้นถ้าสตริงว่างเปล่า lnจะเป็น -1 บันทึกสำหรับความจริงsize_tก็คือไม่ได้ลงชื่อดังนั้นจึงเขียนไปยังหน่วยความจำแบบสุ่ม ฉันคิดว่าคุณต้องการใช้ssize_tและตรวจสอบlnคือ> 0
abligh

2
@ legends2k: การค้นหาค่าเวลาคอมไพล์ (โดยเฉพาะอย่างยิ่งค่าศูนย์ในstrlen) สามารถดำเนินการได้อย่างมีประสิทธิภาพมากกว่าการค้นหาแบบ char-by-char แบบธรรมดา ด้วยเหตุผลใดฉันจึงพิจารณาโซลูชันนี้ดีกว่าstrchrหรือstrcspnตาม
AnT

17

ด้านล่างนี้เป็นวิธีที่รวดเร็วในการลบที่อาจเกิดขึ้นจากสตริงที่บันทึกไว้โดย'\n' มันใช้กับ 2 การทดสอบfgets()
strlen()

char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) != NULL) {

  size_t len = strlen(buffer);
  if (len > 0 && buffer[len-1] == '\n') {
    buffer[--len] = '\0';
  }

ตอนนี้ใช้bufferและlenตามความจำเป็น

วิธีนี้มีประโยชน์ด้านข้างของlenค่าสำหรับรหัสที่ตามมา strchr(Name, '\n')มันอาจจะเป็นได้อย่างง่ายดายได้เร็วกว่า อ้างอิง YMMV แต่วิธีการทั้งสองทำงาน


bufferจากเดิมfgets()จะไม่ได้มีใน"\n"บางสถานการณ์:
A) สายยาวเกินไปสำหรับbufferเท่านั้นดังนั้นcharก่อนที่จะถูกบันทึกไว้ใน'\n' bufferอักขระที่ยังไม่ได้อ่านยังคงอยู่ในกระแส
B) '\n'บรรทัดสุดท้ายในไฟล์ไม่ได้จบลงด้วยการ

หากอินพุตมีการฝังอักขระ null '\0'ไว้ที่ใดที่หนึ่งความยาวที่รายงานโดยstrlen()จะไม่รวม'\n'ตำแหน่ง


ปัญหาคำตอบอื่น ๆ :

  1. strtok(buffer, "\n");ล้มเหลวในการลบ'\n'เมื่อเป็นbuffer "\n"จากคำตอบนี้- แก้ไขหลังจากคำตอบนี้เพื่อเตือนถึงข้อ จำกัด นี้

  2. ต่อไปนี้จะล้มเหลวในโอกาสที่หายากเมื่อครั้งแรกที่charอ่านโดยเป็นfgets() นี้เกิดขึ้นเมื่อการป้อนข้อมูลเริ่มต้นด้วยการฝังตัว'\0' '\0'จากนั้นก็buffer[len -1]จะกลายเป็นเข้าถึงหน่วยความจำอย่างแน่นอนนอกช่วงที่ถูกต้องของbuffer[SIZE_MAX] bufferบางสิ่งที่แฮ็กเกอร์อาจลองหรือพบเจอในการอ่านไฟล์ข้อความ UTF16 ที่โง่เขลา นี่คือสถานะของคำตอบเมื่อเขียนคำตอบนี้ หลังจากนั้นผู้ที่ไม่ได้เป็น OP จะแก้ไขเพื่อรวมรหัสเช่นคำตอบของการตรวจสอบ""นี้

    size_t len = strlen(buffer);
    if (buffer[len - 1] == '\n') {  // FAILS when len == 0
      buffer[len -1] = '\0';
    }
  3. sprintf(buffer,"%s",buffer);เป็นพฤติกรรมที่ไม่ได้กำหนด: Ref นอกจากนี้จะไม่บันทึกช่องว่างนำหน้าแยกหรือตามหลัง ตอนนี้ลบ

  4. [แก้ไขเนื่องจากคำตอบที่ดีในภายหลัง] ไม่มีปัญหากับสายการบินbuffer[strcspn(buffer, "\n")] = 0;อื่นนอกเหนือจากประสิทธิภาพเมื่อเทียบกับstrlen()วิธีการ ประสิทธิภาพในการตัดแต่งมักจะไม่เป็นปัญหากับรหัสที่กำหนดคือ I / O - หลุมดำของเวลา CPU หากรหัสต่อไปนี้ต้องการความยาวของสตริงหรือมีประสิทธิภาพสูงให้ใช้strlen()วิธีนี้ อื่นstrcspn()เป็นทางเลือกที่ดี


ขอบคุณสำหรับคำตอบที่เป็นประโยชน์ เราสามารถใช้strlen(buffer)เมื่อขนาดบัฟเฟอร์ถูกจัดสรรโดยใช้แบบไดนามิกได้mallocหรือไม่?
rrz0

@ Rrz0 buffer = malloc(allocation_size); length = strlen(buffer);ไม่ดี - ข้อมูลในหน่วยความจำที่ชี้ไปยังbufferไม่รู้จัก buffer = malloc(allocation_size_4_or_more); strcpy(buffer, "abc"); length = strlen(buffer);ตกลง
chux - Reinstate Monica

ขอบคุณสำหรับสิ่งนี้!! ฉันกำลังเรียนหลักสูตร CS และสิ่งนี้มีประโยชน์อย่างมากสำหรับงานที่ได้รับมอบหมาย ฉันให้เครดิตคำตอบของคุณในซอร์สโค้ด
นาธาเนียลฮอยต์

8

ตรงไปยังลบ '\ n' จากเอาต์พุตของ fgets หากทุกบรรทัดมี '\ n'

line[strlen(line) - 1] = '\0';

มิฉะนั้น:

void remove_newline_ch(char *line)
{
    int new_line = strlen(line) -1;
    if (line[new_line] == '\n')
        line[new_line] = '\0';
}

1
หมายเหตุ: มันจะเป็นที่ปลอดภัยที่จะใช้แทนstrnlen strlen
Mike Mertsock

3
ความคิดเห็นเกี่ยวกับคำตอบแรกในคำถามที่เชื่อมโยงสถานะ "โปรดทราบว่า strlen (), strcmp () และ strdup () มีความปลอดภัยทางเลือก 'n' ช่วยให้คุณสามารถใช้ฟังก์ชันเพิ่มเติมได้"
Étienne

4
@ esker ไม่มันจะไม่ การแทรกnไม่เพิ่มความปลอดภัยอย่างน่าอัศจรรย์ในกรณีนี้จริง ๆ แล้วจะทำให้รหัสอันตรายมากขึ้น strncpyฟังก์ชั่นที่ไม่ปลอดภัยอย่างยิ่งเช่นเดียวกันกับ โพสต์ที่คุณลิงก์ไปนั้นเป็นคำแนะนำที่ไม่ดี
MM

3
สิ่งนี้ล้มเหลวอย่างน่าสมเพชสำหรับสตริงที่ว่างเปล่า ( "") นอกจากนี้ยังมีstrlen()ผลตอบแทนไม่ได้size_t int
alk

4
สิ่งนี้ไม่ปลอดภัยสำหรับสตริงว่างมันจะเขียนที่ดัชนี -1 อย่าใช้สิ่งนี้
Jean-François Fabre

3

สำหรับการตัดแต่งแบบเดี่ยว '\ n'

void remove_new_line(char* string)
{
    size_t length = strlen(string);
    if((length > 0) && (string[length-1] == '\n'))
    {
        string[length-1] ='\0';
    }
}

สำหรับการเล็ม '\ n' หลายครั้ง

void remove_multi_new_line(char* string)
{
  size_t length = strlen(string);
  while((length>0) && (string[length-1] == '\n'))
  {
      --length;
      string[length] ='\0';
  }
}

1
ทำไมรังifเมื่อคุณก็สามารถเขียนเงื่อนไขการใช้&&? ที่whileห่วงมีโครงสร้างที่แปลก while (length > 0 && string[length-1] == '\n') { --length; string[length] = '\0'; }มันก็อาจจะเป็น
melpomene

@ melpomene ขอบคุณสำหรับคำแนะนำ อัปเดตรหัส
BEPP

1
size_t length = strlen(string); if (length > 0 && string[length-1] == '\n') { string[length-1] = '\0'; }ผมขอแนะนำว่าฟังก์ชั่นแรกที่ถูกกำหนดขึ้นตามธรรมชาติเช่น สิ่งนี้จะสะท้อนนิยามที่สองที่ดีกว่า (เพียงแค่ใช้ifแทนwhile)
melpomene

@elpomene ขอบคุณ มันทำให้รู้สึก ฉันอัปเดตรหัสแล้ว
BEPP

1

My Newbie way ;-) โปรดแจ้งให้เราทราบหากถูกต้อง ดูเหมือนว่าจะทำงานได้กับทุกกรณีของฉัน:

#define IPT_SIZE 5

int findNULL(char* arr)
{
    for (int i = 0; i < strlen(arr); i++)
    {
        if (*(arr+i) == '\n')
        {
            return i;
        }
    }
    return 0;
}

int main()
{
    char *input = malloc(IPT_SIZE + 1 * sizeof(char)), buff;
    int counter = 0;

    //prompt user for the input:
    printf("input string no longer than %i characters: ", IPT_SIZE);
    do
    {
        fgets(input, 1000, stdin);
        *(input + findNULL(input)) = '\0';
        if (strlen(input) > IPT_SIZE)
        {
            printf("error! the given string is too large. try again...\n");
            counter++;
        }
        //if the counter exceeds 3, exit the program (custom function):
        errorMsgExit(counter, 3); 
    }
    while (strlen(input) > IPT_SIZE);

//rest of the program follows

free(input)
return 0;
}

1

ขั้นตอนในการลบอักขระบรรทัดใหม่ด้วยวิธีที่ชัดเจนที่สุด:

  1. กำหนดความยาวของภายในสตริงNAMEโดยใช้ส่วนหัวstrlen() string.hโปรดทราบว่าไม่นับยุติstrlen()\0
size_t sl = strlen(NAME);

  1. ดูว่าสตริงเริ่มต้นด้วยหรือมีเพียงหนึ่ง\0ตัวอักษร (สตริงว่าง) ในกรณีนี้slจะเป็น0เพราะstrlen()อย่างที่ฉันได้กล่าวไว้ข้างต้นไม่นับ\0และหยุดเมื่อเกิดขึ้นครั้งแรก:
if(sl == 0)
{
   // Skip the newline replacement process.
}

  1. '\n'ตรวจสอบว่าตัวอักษรตัวสุดท้ายของสายที่เหมาะสมเป็นตัวละครขึ้นบรรทัดใหม่ หากเป็นกรณีนี้แทนด้วย\n \0โปรดทราบว่าการนับดัชนีเริ่มต้นที่0เราจะต้องทำNAME[sl - 1]:
if(NAME[sl - 1] == '\n')
{
   NAME[sl - 1] = '\0';
}

หมายเหตุหากคุณกด Enter ที่fgets()คำขอสตริงเท่านั้น (เนื้อหาสตริงประกอบด้วยอักขระขึ้นบรรทัดใหม่เท่านั้น) สตริงในNAMEจะเป็นสตริงว่างหลังจากนั้น


  1. เราสามารถรวมขั้นตอนที่ 2 และ 3 เข้าด้วยกันในสถานะเดียวifโดยใช้ตัวดำเนินการตรรกะ&&:
if(sl > 0 && NAME[sl - 1] == '\n')
{
   NAME[sl - 1] = '\0';
}

  1. รหัสสำเร็จรูป:
size_t sl = strlen(NAME);
if(sl > 0 && NAME[sl - 1] == '\n')
{
   NAME[sl - 1] = '\0';
}

หากคุณต้องการฟังก์ชั่นสำหรับใช้เทคนิคนี้โดยการจัดการfgetsสตริงเอาต์พุตโดยทั่วไปโดยไม่ต้องพิมพ์ซ้ำทุกครั้งนี่คือfgets_newline_kill:

void fgets_newline_kill(char a[])
{
    size_t sl = strlen(a);

    if(sl > 0 && a[sl - 1] == '\n')
    {
       a[sl - 1] = '\0';
    }
}

ในตัวอย่างที่คุณให้มามันจะเป็น:

printf("Enter your Name: ");

if (fgets(Name, sizeof Name, stdin) == NULL) {
    fprintf(stderr, "Error reading Name.\n");
    exit(1);
}
else {
    fgets_newline_kill(NAME);
}

โปรดทราบว่าวิธีนี้ใช้ไม่ได้หากสตริงอินพุตมีการฝัง\0s ไว้ หากที่จะเป็นกรณีเดียวที่จะกลับมาจำนวนตัวอักษรจนกว่าแรกstrlen() \0แต่นี่ไม่ใช่วิธีการทั่วไปเนื่องจากฟังก์ชั่นการอ่านสตริงส่วนใหญ่มักจะหยุดที่แรก\0และใช้สตริงจนกระทั่งตัวอักษรเป็นโมฆะ

นอกเหนือจากคำถามด้วยตัวมันเอง พยายามหลีกเลี่ยงการ negations คู่ที่ทำให้ unclearer if (!(fgets(Name, sizeof Name, stdin) != NULL) {}รหัสของคุณ: if (fgets(Name, sizeof Name, stdin) == NULL) {}คุณก็สามารถทำได้


ไม่แน่ใจว่าทำไมคุณถึงต้องการทำเช่นนี้ จุดของการลบบรรทัดใหม่ไม่ได้เป็นการสิ้นสุดสตริงเป็นโมฆะ มันคือการลบบรรทัดใหม่ การแทนที่\nด้วย a \0ที่ท้ายของสตริงเป็นวิธีการ "ลบ" บรรทัดใหม่ แต่การแทนที่\nอักขระภายในสตริงจะเป็นการเปลี่ยนสตริง ไม่ใช่เรื่องแปลกที่จะมีสตริงที่มีอักขระขึ้นบรรทัดใหม่หลายตัวโดยเจตนาและสิ่งนี้จะมีประสิทธิภาพในการตัดปลายของสตริงเหล่านั้น การลบ\nบรรทัดใหม่เช่นเนื้อหาอาร์เรย์ต้องการที่จะเปลี่ยนจากซ้ายไปมากกว่าเขียน
อดีต nihilo

@exnihilo บางคนสามารถป้อนสตริงด้วยการขึ้นบรรทัดใหม่หลายรายการโดยใช้ได้fgets()อย่างไร
RobertS สนับสนุน Monica Cellio

คุณอาจเชื่อมสตริงที่ได้จากการโทรหลายสายเข้าfgets()ด้วยกัน แต่ฉันไม่เข้าใจคำคัดค้านของคุณ: คุณเป็นคนเดียวที่เสนอรหัสเพื่อจัดการกับการขึ้นบรรทัดใหม่หลายรายการ
อดีต nihilo

@ exnihilo คุณพูดถูกฉันจะคิดกลยุทธ์ ฉันแค่ต้องการเพิ่มวิธีที่รุนแรง แต่เป็นไปได้เพื่อให้ได้ผลลัพธ์ที่ต้องการ
RobertS สนับสนุน Monica Cellio

@exnihilo แก้ไขคำตอบของฉันโดยสมบูรณ์และทำตามวิธีการหลักโดยใช้strlenเป็นต้นเหตุผลในการไม่ซ้ำซ้อน: 1. คำอธิบายของโค้ดตามขั้นตอน 2. จัดทำขึ้นเป็นฟังก์ชั่นและการแก้ปัญหาตามบริบท 3. คำแนะนำเพื่อหลีกเลี่ยงการแสดงออกการปฏิเสธคู่
RobertS สนับสนุน Monica Cellio

0

Tim เป็นหนึ่งในสายการบินที่ยอดเยี่ยมสำหรับสตริงที่ได้รับจากการเรียกไปยัง fgets เพราะคุณรู้ว่ามันมีการขึ้นบรรทัดใหม่เพียงครั้งเดียวในตอนท้าย

หากคุณอยู่ในบริบทที่แตกต่างและต้องการจัดการสตริงที่อาจมีมากกว่าหนึ่งบรรทัดใหม่คุณอาจกำลังมองหา strrspn มันไม่ได้เป็น POSIX ซึ่งหมายความว่าคุณจะไม่พบมันในทุก Unices ฉันเขียนหนึ่งสำหรับความต้องการของฉันเอง

/* Returns the length of the segment leading to the last 
   characters of s in accept. */
size_t strrspn (const char *s, const char *accept)
{
  const char *ch;
  size_t len = strlen(s);

more: 
  if (len > 0) {
    for (ch = accept ; *ch != 0 ; ch++) {
      if (s[len - 1] == *ch) {
        len--;
        goto more;
      }
    }
  }
  return len;
}

สำหรับผู้ที่มองหา Perl chomp ที่เทียบเท่าใน C ฉันคิดว่ามันเป็นเช่นนั้น (chomp ลบบรรทัดใหม่ต่อท้ายเท่านั้น)

line[strrspn(string, "\r\n")] = 0;

ฟังก์ชั่น strrcspn:

/* Returns the length of the segment leading to the last 
   character of reject in s. */
size_t strrcspn (const char *s, const char *reject)
{
  const char *ch;
  size_t len = strlen(s);
  size_t origlen = len;

  while (len > 0) {
    for (ch = reject ; *ch != 0 ; ch++) {
      if (s[len - 1] == *ch) {
        return len;
      }
    }
    len--;
  }
  return origlen;
}

1
"เพราะคุณรู้ว่าพวกเขามีขึ้นบรรทัดใหม่เดียวในตอนท้าย" -> มันสามารถทำงานได้แม้ไม่มี'\n'(หรือถ้าเป็นสตริง"")
chux - Reinstate Monica

ในการตอบกลับความคิดเห็นแรกของคุณคำตอบของฉันเก็บไว้ ผมต้องโยน resetlen ในเมื่อไม่มีstrrcspn \n
Philippe A.

ทำไมต้องใช้goto end;แทนreturn len;?
chqrlie

@ chqrlie ฉันจำเป็นต้องออกจากลูป 2 ระดับที่ไม่เหมาะสมนี้ที่ฉันได้รับ อันตรายได้ทำไปแล้ว ทำไมไม่ข้ามไป
Philippe A.

คุณมีสองประเภทgotoในรหัสของคุณ: ไร้ประโยชน์gotoที่สามารถแทนที่ด้วยreturnคำสั่งและถอยหลังgotoที่ถือว่าชั่วร้าย การใช้strchrช่วยทำให้เกิดผลstrrspnและstrrcspnทำได้ง่ายกว่า: size_t strrspn(const char *s, const char *accept) { size_t len = strlen(s); while (len > 0 && strchr(accept, s[len - 1])) { len--; } return len; }และsize_t strrcspn(const char *s, const char *reject) { size_t len = strlen(s); while (len > 0 && !strchr(reject, s[len - 1])) { len--; } return len; }
chqrlie

0

หากใช้getlineเป็นตัวเลือก - ไม่ละเลยปัญหาด้านความปลอดภัยและหากคุณต้องการรั้งตัวชี้ - คุณสามารถหลีกเลี่ยงการใช้ฟังก์ชันสตริงได้เนื่องจากgetlineจะส่งคืนจำนวนอักขระ สิ่งที่ชอบด้านล่าง

#include<stdio.h>
#include<stdlib.h>
int main(){
char *fname,*lname;
size_t size=32,nchar; // Max size of strings and number of characters read
fname=malloc(size*sizeof *fname);
lname=malloc(size*sizeof *lname);
if(NULL == fname || NULL == lname){
 printf("Error in memory allocation.");
 exit(1);
}
printf("Enter first name ");
nchar=getline(&fname,&size,stdin);
if(nchar == -1){ // getline return -1 on failure to read a line.
 printf("Line couldn't be read.."); 
 // This if block could be repeated for next getline too
 exit(1);
}
printf("Number of characters read :%zu\n",nchar);
fname[nchar-1]='\0';
printf("Enter last name ");
nchar=getline(&lname,&size,stdin);
printf("Number of characters read :%zu\n",nchar);
lname[nchar-1]='\0';
printf("Name entered %s %s\n",fname,lname);
return 0;
}

หมายเหตุ : ผู้[ ปัญหาด้านความปลอดภัย ]ด้วยgetlineไม่ควรละเลยแม้ว่า


-1

ฟังก์ชั่นด้านล่างเป็นส่วนหนึ่งของห้องสมุดการประมวลผลสตริงที่ฉันยังคงอยู่ใน Github มันจะลบและอักขระที่ไม่ต้องการออกจากสตริงตรงตามที่คุณต้องการ

int zstring_search_chr(const char *token,char s){
    if (!token || s=='\0')
        return 0;

    for (;*token; token++)
        if (*token == s)
            return 1;

    return 0;
}

char *zstring_remove_chr(char *str,const char *bad) {
    char *src = str , *dst = str;
    while(*src)
        if(zstring_search_chr(bad,*src))
            src++;
        else
            *dst++ = *src++;  /* assign first, then incement */

    *dst='\0';
        return str;
}

ตัวอย่างการใช้งานอาจเป็น

Example Usage
      char s[]="this is a trial string to test the function.";
      char const *d=" .";
      printf("%s\n",zstring_remove_chr(s,d));

  Example Output
      thisisatrialstringtotestthefunction

คุณอาจต้องการตรวจสอบฟังก์ชั่นอื่น ๆ ที่มีอยู่หรือแม้กระทั่งมีส่วนร่วมในโครงการ :) https://github.com/fnoyanisi/zString


คุณควรลบ*ใน*src++;และทำให้bad, และtoken d const char *ยังทำไมไม่ใช้strchrแทนzChrSearch? *srcไม่สามารถ'\0'อยู่ในการzStrrmvทำงานของคุณ
chqrlie

ขอบคุณ @chqrlie! อัปเดตรหัสเพื่อให้สอดคล้องกับข้อเสนอแนะของคุณ ..... zstring เริ่มต้นเป็นโครงการที่สนุกโดยมีจุดประสงค์ในการสร้างไลบรารี่การจัดการสตริงโดยไม่ต้องใช้ฟังก์ชั่นไลบรารีมาตรฐานใด ๆ ดังนั้นฉันจึงไม่ได้ใช้strchr
fnisi

1
การเขียน " ไลบรารีการจัดการสตริงโดยไม่ใช้ฟังก์ชั่นไลบรารีมาตรฐาน " เป็นการออกกำลังกายที่ดี แต่ทำไมบอกคนอื่น ๆ ให้ใช้? ถ้ามีอะไรมันจะช้าลงและทดสอบน้อยกว่าไลบรารีมาตรฐานใด ๆ
melpomene

นี่เป็นการทำงานที่แตกต่างจากคำถามที่ถาม อาจใช้เพื่อกำจัดบรรทัดใหม่เท่านั้น แต่รู้สึกเหมือน overkill
Jonathan Leffler

-1
 for(int i = 0; i < strlen(Name); i++ )
{
    if(Name[i] == '\n') Name[i] = '\0';
}

คุณควรจะลองดู รหัสนี้โดยทั่วไปวนรอบสตริงจนกระทั่งพบ '\ n' เมื่อพบว่า '\ n' จะถูกแทนที่ด้วยตัวสิ้นสุดเทอร์มินัลอักขระ '\ 0'

โปรดทราบว่าคุณกำลังเปรียบเทียบอักขระและไม่ใช้สตริงในบรรทัดนี้ดังนั้นไม่จำเป็นต้องใช้ strcmp ():

if(Name[i] == '\n') Name[i] = '\0';

เนื่องจากคุณจะใช้เครื่องหมายคำพูดเดี่ยวและไม่ใช่เครื่องหมายคำพูดคู่ นี่คือลิงค์เกี่ยวกับอัญประกาศคู่ vs Single หากคุณต้องการทราบข้อมูลเพิ่มเติม


2
มันจะดีกว่าถ้าคุณอธิบายและแก้ไขรูปแบบของรหัสของคุณ
Anh Pham

โดยปกติแล้วจะเป็นการดีกว่าที่จะอธิบายวิธีแก้ปัญหาแทนที่จะเพียงแค่โพสต์แถวของรหัสที่ไม่ระบุชื่อ คุณสามารถอ่านวิธีทำผมเขียนคำตอบที่ดีและยังอธิบายทั้งหมดตอบรหัสตาม
Massimiliano Kraus

1
ฉันขอโทษนี่เป็นผลงานชิ้นแรกของฉันที่นี่ ฉันจะซ่อมมัน. ขอบคุณสำหรับคำติชม
Matheus Martins Jerônimo

3
ไม่มีประสิทธิภาพ: for(int i = 0; i < strlen(Name); i++ )จะเรียกstrlen(Name)หลายครั้ง (การเปลี่ยนแปลงลูป Name[]) ดังนั้นด้วยความยาวNนี่เป็นO(N*N)วิธีแก้ปัญหา strlen(Name)จำเป็นต้องโทรเพียง 1 ครั้งเท่านั้นเพื่อจัดเตรียมโซลูชัน O (N) ` ไม่ชัดเจนว่าทำไมใช้แทนint i size_t iข้อควรพิจารณาfor(size_t i = 0; i < Name[i]; i++ )
chux - Reinstate Monica

@chux ชอบมากขึ้นfor (size_t i = 0; Name[i]; i++) { if (Name[i] == '\n') { Name[i] = '\0'; break; } }
melpomene

-1

ลองอันนี้:

        int remove_cr_lf(char *str)
        {
          int len =0;


          len = strlen(str);

          for(int i=0;i<5;i++)
          {
            if (len>0)
            if (str[len-1] == '\n')
            {
              str[len-1] = 0;
              len--;
            }

            if (len>0)
            if (str[len-1] == '\r')
            {
              str[len-1] = 0;
              len--;
            }
          }

          return 0;
        }

1
len = strlen(str)อาจล้น: strlenผลตอบแทนไม่ได้size_t เงื่อนไขอะไรintแปลก ๆ if (len>0) if (...)คุณไม่รู้เกี่ยวกับ&&? หากคุณกำลังจะลบอินสแตนซ์ของ CR / LF ที่ต่อท้ายหลายรายการทำไมถึง จำกัด ตัวเองถึง 5 ทำไมไม่ลบทั้งหมดออก? ทำไมฟังก์ชั่นถึงมีintประเภทกลับเมื่อมันกลับมาตลอด0? ทำไมไม่กลับมาvoid?
melpomene
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.