ฉันจะเปรียบเทียบสตริงได้อย่างถูกต้องได้อย่างไร


182

ฉันพยายามรับโปรแกรมเพื่อให้ผู้ใช้ป้อนคำหรือตัวอักษรเก็บไว้แล้วพิมพ์จนกระทั่งผู้ใช้พิมพ์อีกครั้งออกจากโปรแกรม รหัสของฉันมีลักษณะเช่นนี้:

#include <stdio.h>

int main()
{
    char input[40];
    char check[40];
    int i=0;
    printf("Hello!\nPlease enter a word or character:\n");
    gets(input);
    printf("I will now repeat this until you type it back to me.\n");

    while (check != input)
    {
        printf("%s\n", input);
        gets(check); 
    }

    printf("Good bye!");


    return 0;
}

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


13
gets( )ถูกลบออกจากมาตรฐาน ใช้fgets( )แทน
Edward Karak

1
โปรดทราบว่าคำตอบนี้เป็นเพราะเหตุใดจึงstrcmp()ส่งกลับค่าศูนย์เมื่ออินพุตที่เท่ากันอธิบายวิธีเปรียบเทียบสตริงสำหรับความเสมอภาคความไม่เท่าเทียมกันน้อยกว่ามากกว่ามากกว่าน้อยกว่าหรือเท่ากับและมากกว่าหรือเท่ากับ การเปรียบเทียบสตริงทั้งหมดไม่ได้มีไว้เพื่อความเท่าเทียมกัน การเปรียบเทียบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่นั้นแตกต่างกันอีกครั้ง การเปรียบเทียบพิเศษอื่น ๆ (ตัวอย่างเช่นพจนานุกรม) ต้องการการเปรียบเทียบที่พิเศษกว่าและมี regexes สำหรับการเปรียบเทียบที่ซับซ้อนยิ่งขึ้น
Jonathan Leffler

โปรดทราบด้วยว่ามีคำถามที่ซ้ำกันเป็นหลักฉันจะตรวจสอบได้อย่างไรว่าค่าตรงกับสตริงที่ถูกถามเมื่อหลายปีก่อน
Jonathan Leffler

สิ่งนี้ตอบคำถามของคุณหรือไม่ ฉันจะตรวจสอบว่าค่าตรงกับสตริงได้อย่างไร
Andreas

คำถามนี้ดี แต่การใช้gets()ไม่มีการไป มันถูกลบออกจากมาตรฐานตั้งแต่ C11 -> โปรดอ่านเหตุใดฟังก์ชั่นการรับจึงอันตรายมากจนไม่ควรใช้
RobertS สนับสนุน Monica Cellio

คำตอบ:


276

คุณไม่สามารถ (ใช้อย่างเป็นประโยชน์) เปรียบเทียบสตริงที่ใช้!=หรือ==คุณต้องใช้strcmp:

while (strcmp(check,input) != 0)

เหตุผลนี้เป็นเพราะ!=และ==จะเปรียบเทียบที่อยู่ฐานของสตริงเหล่านั้นเท่านั้น ไม่ใช่เนื้อหาของสตริงเอง


10
เหมือนกันใน java ซึ่งอาจเปรียบเทียบกับที่อยู่
Telerik

29
การเขียนwhile (strcmp(check, input))มีเพียงพอและถือว่าเป็นแนวปฏิบัติที่ดี
Shiva

ทราบข้อมูลเพิ่มเติม ... codificare.in/codes/c/…
chanu panwar

7
มันปลอดภัยกว่าที่จะใช้ strncmp! ไม่ต้องการบัฟเฟอร์ล้น!
Floam

@Floam หากคุณไม่มีสตริงจริง ๆ แต่ลำดับที่ไม่ได้เป็นศูนย์ของอักขระที่ไม่เป็นศูนย์ที่มีความยาวที่รู้จักแน่นอนว่านั่นจะเป็นคาถาที่เหมาะสม แต่นั่นเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง!
Deduplicator

33

ตกลงสิ่งต่าง ๆ : getsไม่ปลอดภัยและควรถูกแทนที่ด้วยfgets(input, sizeof(input), stdin)เพื่อให้คุณไม่ได้รับบัฟเฟอร์ล้น

ถัดไปในการเปรียบเทียบสตริงคุณต้องใช้strcmpโดยที่ค่าส่งคืนเป็น 0 ระบุว่าทั้งสองสตริงตรงกัน การใช้ตัวดำเนินการความเสมอภาค (เช่น!=) เปรียบเทียบที่อยู่ของสองสายตรงข้ามกับแต่ละตัวcharที่อยู่ข้างใน

และโปรดทราบว่าในตัวอย่างนี้จะไม่ทำให้เกิดปัญหาfgetsเก็บตัวอักขระขึ้นบรรทัดใหม่'\n'ในบัฟเฟอร์ด้วย gets()ไม่. หากคุณเปรียบเทียบอินพุตของผู้ใช้จากfgets()กับตัวอักษรสตริงเช่น"abc"จะไม่ตรงกัน (เว้นแต่บัฟเฟอร์มีขนาดเล็กเกินไปเพื่อที่'\n'จะไม่พอดีกับมัน)


คุณช่วยอธิบายความสัมพันธ์ / ปัญหาของ "\ n" และตัวอักษรสตริงได้ไหม? ฉันได้รับผลลัพธ์ไม่เท่ากันเมื่อเปรียบเทียบสตริง (บรรทัด) ของไฟล์กับไฟล์อื่นทั้งหมด
ไร้ความสามารถ

@incompetent - หากคุณอ่านบรรทัดจากไฟล์ด้วยfgets()สตริงอาจจะเป็น"abc\n"เพราะfgets()ขึ้นบรรทัดใหม่ หากคุณเปรียบเทียบกับ"abc"คุณจะได้รับ 'ไม่เท่ากัน' เนื่องจากความแตกต่างระหว่างการยกเลิกแบบ null ไบต์"abc"และการขึ้นบรรทัดใหม่ในข้อมูลการอ่าน ดังนั้นคุณต้องรีบขึ้นบรรทัดใหม่ วิธีการหนึ่งบรรทัดที่เชื่อถือได้ในการทำสิ่งนั้นbuffer[strcspn(buffer, "\n")] = '\0';ซึ่งมีข้อดีของการทำงานอย่างถูกต้องโดยไม่คำนึงว่ามีข้อมูลใด ๆ ในบัฟเฟอร์หรือว่าข้อมูลนั้นลงท้ายด้วยขึ้นบรรทัดใหม่หรือไม่ วิธีอื่น ๆ ในการขึ้นบรรทัดใหม่ขึ้นบรรทัดใหม่ล้มเหลวได้อย่างง่ายดาย
Jonathan Leffler

คำตอบนี้กล่าวถึงปัญหาของรหัสอย่างถูกต้องในขณะที่คำตอบ upvoted และคำตอบที่ยอมรับมากที่สุดครอบคลุมเฉพาะเพื่อตอบคำถามของคำถาม โดยเฉพาะการพูดถึงย่อหน้าสุดท้ายนั้นสุดยอด +1
RobertS สนับสนุน Monica Cellio

11

strcmpใช้

นี่คือในstring.hห้องสมุดและเป็นที่นิยมมาก strcmpกลับ 0 ถ้าสตริงเท่ากัน ดูสิ่งนี้สำหรับคำอธิบายที่ดีขึ้นเกี่ยวกับสิ่งที่strcmpส่งคืน

โดยทั่วไปคุณต้องทำ:

while (strcmp(check,input) != 0)

หรือ

while (!strcmp(check,input))

หรือ

while (strcmp(check,input))

คุณสามารถตรวจสอบนี้ , strcmpกวดวิชาใน


7

คุณไม่สามารถเปรียบเทียบอาร์เรย์ได้โดยตรงเช่นนี้

array1==array2

คุณควรเปรียบเทียบพวกเขาด้วยถ่านโดยถ่าน; สำหรับสิ่งนี้คุณสามารถใช้ฟังก์ชันและส่งคืนค่าบูลีน (True: 1, False: 0) จากนั้นคุณสามารถใช้มันในสภาพการทดสอบของลูป while

ลองสิ่งนี้:

#include <stdio.h>
int checker(char input[],char check[]);
int main()
{
    char input[40];
    char check[40];
    int i=0;
    printf("Hello!\nPlease enter a word or character:\n");
    scanf("%s",input);
    printf("I will now repeat this until you type it back to me.\n");
    scanf("%s",check);

    while (!checker(input,check))
    {
        printf("%s\n", input);
        scanf("%s",check);
    }

    printf("Good bye!");

    return 0;
}

int checker(char input[],char check[])
{
    int i,result=1;
    for(i=0; input[i]!='\0' || check[i]!='\0'; i++) {
        if(input[i] != check[i]) {
            result=0;
            break;
        }
    }
    return result;
}

1
คุณช่วยเพิ่มรายละเอียดเกี่ยวกับโซลูชันของคุณได้ไหม
abarisone

ใช่นี่คือการแทนที่สำหรับฟังก์ชั่น strcmp และการแก้ปัญหาโดยไม่ต้องใช้ string.h header @Jongware
mugetsu

2
สิ่งนี้ใช้ไม่ได้ เมื่อcheckerพบว่าในหนึ่งของสตริงก็ไม่ได้ตรวจสอบสตริงอีก'\0' '\0'ฟังก์ชันส่งคืน1("เท่ากับ") แม้ว่าหนึ่งสตริงจะเป็นเพียงคำนำหน้าของอีกอันหนึ่ง (ตัวอย่างเช่น"foo"และ"foobar")
lukasrozs

1
ผมจะใช้แทน|| &&
lukasrozs

3

ยินดีต้อนรับสู่แนวคิดของตัวชี้รุ่นของโปรแกรมเมอร์เริ่มต้นได้พบแนวคิดที่เข้าใจยาก แต่ถ้าคุณต้องการที่จะเติบโตเป็นโปรแกรมเมอร์ที่มีความสามารถคุณจะต้องเข้าใจแนวคิดนี้ในที่สุดและยิ่งไปกว่านั้นคุณกำลังถามคำถามที่ถูกต้องแล้ว ดีแล้ว.

คุณชัดเจนว่าที่อยู่คืออะไร? ดูแผนภาพนี้:

----------     ----------
| 0x4000 |     | 0x4004 |
|    1   |     |    7   |
----------     ----------

ในแผนภาพจำนวนเต็ม 1 จะถูกเก็บไว้ในหน่วยความจำตามที่อยู่ 0x4000 ทำไมถึงอยู่ที่? เนื่องจากหน่วยความจำมีขนาดใหญ่และสามารถเก็บจำนวนเต็มได้มากมายเช่นเดียวกับเมืองที่มีขนาดใหญ่และสามารถเป็นที่ตั้งของครอบครัวจำนวนมาก จำนวนเต็มแต่ละค่าจะถูกเก็บไว้ในตำแหน่งหน่วยความจำเนื่องจากแต่ละตระกูลอยู่ในบ้าน ที่อยู่หน่วยความจำแต่ละแห่งจะถูกระบุด้วยที่อยู่เนื่องจากบ้านแต่ละหลังจะถูกระบุด้วยที่อยู่

กล่องสองกล่องในแผนภาพแสดงตำแหน่งหน่วยความจำที่แตกต่างกันสองแห่ง คุณสามารถคิดถึงพวกเขาราวกับว่าพวกเขาเป็นบ้าน จำนวนเต็ม 1 อยู่ในตำแหน่งหน่วยความจำตามที่อยู่ 0x4000 (คิดว่า "4000 Elm St. ") เลขจำนวนเต็ม 7 อยู่ในตำแหน่งหน่วยความจำตามที่อยู่ 0x4004 (คิดว่า "4004 Elm St. ")

คุณคิดว่าโปรแกรมของคุณกำลังเปรียบเทียบ 1 กับ 7 แต่ไม่ใช่ มันเปรียบเทียบ 0x4000 กับ 0x4004 ดังนั้นจะเกิดอะไรขึ้นเมื่อคุณมีสถานการณ์เช่นนี้?

----------     ----------
| 0x4000 |     | 0x4004 |
|    1   |     |    1   |
----------     ----------

จำนวนเต็มสองจำนวนเหมือนกัน แต่ที่อยู่แตกต่างกัน โปรแกรมของคุณเปรียบเทียบที่อยู่


2

เมื่อใดก็ตามที่คุณพยายามเปรียบเทียบสตริงเปรียบเทียบกับอักขระแต่ละตัว สำหรับสิ่งนี้คุณสามารถใช้ฟังก์ชันสตริงในตัวที่เรียกว่า strcmp (input1, input2); และคุณควรใช้ไฟล์ส่วนหัวที่เรียกว่า#include<string.h>

ลองรหัสนี้:

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h>  

int main() 
{ 
    char s[]="STACKOVERFLOW";
    char s1[200];
    printf("Enter the string to be checked\n");//enter the input string
    scanf("%s",s1);
    if(strcmp(s,s1)==0)//compare both the strings  
    {
        printf("Both the Strings match\n"); 
    } 
    else
    {
        printf("Entered String does not match\n");  
    } 
    system("pause");  
} 

0

ฉันจะเปรียบเทียบสตริงได้อย่างถูกต้องได้อย่างไร

char input[40];
char check[40];
strcpy(input, "Hello"); // input assigned somehow
strcpy(check, "Hello"); // check assigned somehow

// insufficient
while (check != input)

// good
while (strcmp(check, input) != 0)
// or 
while (strcmp(check, input))

ขอให้เราขุดลึกเพื่อดูว่าทำไมcheck != inputไม่เพียงพอ

ใน C สตริงเป็นข้อกำหนดของไลบรารีมาตรฐาน

สตริงเป็นลำดับต่อเนื่องกันของตัวละครและยกเลิกโดยรวมทั้งอักขระ null แรก
C11 §7.1.1 1

inputดังกล่าวข้างต้นไม่ได้เป็นสตริง inputคืออาร์เรย์ 40 ของถ่านของถ่าน

เนื้อหาของinputจะกลายเป็นสตริง

ในกรณีส่วนใหญ่เมื่อมีการใช้อาร์เรย์ในนิพจน์มันจะถูกแปลงเป็นที่อยู่ขององค์ประกอบที่ 1

ด้านล่างจะแปลงcheckและinputที่อยู่ตามลำดับขององค์ประกอบแรกจากนั้นจะทำการเปรียบเทียบที่อยู่เหล่านั้น

check != input   // Compare addresses, not the contents of what addresses reference

ในการเปรียบเทียบสตริงเราต้องใช้ที่อยู่เหล่านั้นแล้วดูข้อมูลที่ชี้ไป ไม่ทำงาน
strcmp() §7.23.4.2

int strcmp(const char *s1, const char *s2);

strcmpฟังก์ชั่นเปรียบเทียบสตริงชี้ไปตามสตริงชี้ไปตามs1s2

strcmpฟังก์ชั่นให้ผลตอบแทนที่สูงจำนวนเต็มกว่าเท่ากับหรือน้อยกว่าศูนย์ตามเป็นสตริงที่ชี้ไปตามนั้นยิ่งใหญ่กว่าเท่ากับหรือน้อยกว่าสตริงชี้ไปตามs1s2

รหัสไม่เพียง แต่จะสามารถค้นพบว่าสายอักขระนั้นมีข้อมูลเหมือนกันหรือไม่

ด้านล่างเป็นจริงเมื่อสตริงแตกต่างกัน

strcmp(check, input) != 0

สำหรับข้อมูลเชิงลึกโปรดดูการสร้างstrcmp()ฟังก์ชั่นของตัวเอง


-2
    #include<stdio.h>
    #include<string.h>
    int main()
    {
        char s1[50],s2[50];
        printf("Enter the character of strings: ");
        gets(s1);
        printf("\nEnter different character of string to repeat: \n");
        while(strcmp(s1,s2))
        {
            printf("%s\n",s1);
            gets(s2);
        }
        return 0;
    }

นี่เป็นโซลูชันที่ง่ายมากที่คุณจะได้รับผลลัพธ์ตามที่คุณต้องการ


2
gets();ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน C ตั้งแต่ C11
chux - Reinstate Monica

2
strcmp(s1,s2)คือ UB เนื่องจากs2ไม่ได้ระบุเนื้อหาในตอนแรก
chux - Reinstate Monica

มันจะดีถ้าคุณสามารถให้ผลลัพธ์ของตัวอย่างนี้ในบางรูปแบบหรืออื่น
not2qubit
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.