วิธีการแยกสตริงเพื่อ int ใน C ++?


261

C + + วิธีการแยกสตริง (ที่กำหนดเป็นถ่าน *) ลงใน int คืออะไร? จัดการข้อผิดพลาดที่แข็งแกร่งและชัดเจนเป็นบวก (แทนกลับเป็นศูนย์ )


21
ตัวอย่างบางส่วนจากตัวอย่างต่อไปนี้: codeproject.com/KB/recipes/Tokenizer.aspxพวกมันมีประสิทธิภาพและสวยงามมาก

@Beh Tou Cheh ถ้าคุณคิดว่ามันเป็นวิธีที่ดีในการแยก int กรุณาโพสต์มันเป็นคำตอบ
Eugene Yokota

คำตอบ:


165

ใน C ++ 11 ใหม่มีฟังก์ชั่นดังนี้: stoi, stol, stoll, stoul และอื่น ๆ

int myNr = std::stoi(myString);

มันจะโยนข้อผิดพลาดในการแปลง

แม้แต่ฟังก์ชั่นใหม่เหล่านี้ยังคงมีปัญหาแบบเดียวกับที่ Dan กล่าวไว้: พวกเขาจะแปลงสตริง "11x" เป็นจำนวนเต็ม "11" อย่างมีความสุข

ดูเพิ่มเติมได้ที่: http://en.cppreference.com/w/cpp/string/basic_string/stol


4
แต่พวกเขายอมรับการโต้เถียงมากกว่านั้นหนึ่งในนั้นชี้ไปที่ size_t ซึ่งหากไม่ใช่โมฆะก็จะถูกตั้งค่าเป็นตัวอักษรที่ไม่กลับด้านแรก
Zharf

ใช่ใช้พารามิเตอร์ตัวที่สองของ std :: stoi คุณสามารถตรวจสอบอินพุตที่ไม่ถูกต้อง คุณยังต้องหมุนฟังก์ชันการแปลงของคุณเองแม้ว่า ...
CC

เช่นเดียวกับคำตอบที่ยอมรับได้ แต่ด้วยฟังก์ชั่นมาตรฐานเหล่านี้ที่จะสะอาดกว่ามาก IMO
Zharf

โปรดจำไว้ว่าอาร์กิวเมนต์ที่สองในฟังก์ชั่นเหล่านี้สามารถใช้ในการบอกได้ว่ามีการแปลงสตริงทั้งหมดหรือไม่ หากผลลัพธ์size_tไม่เท่ากับความยาวของสตริงแสดงว่าหยุดลงเร็ว มันจะยังคงส่งคืน 11 ในกรณีนั้น แต่posจะเป็น 2 แทนความยาวสตริง 3 coliru.stacked-crooked.com/a/cabe25d64d2ffa29
Zoe

204

ไม่ควรทำอะไร

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

นี่คือวิธีการที่ใช้งานได้ตามสัญชาตญาณดูเหมือนว่า:

bool str2int (int &i, char const *s)
{
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail()) {
        // not an integer
        return false;
    }
    return true;
}

นี้มีปัญหาใหญ่: str2int(i, "1337h4x0r")มีความสุขจะกลับมาtrueและจะได้รับความคุ้มค่าi 1337เราสามารถแก้ไขปัญหานี้ได้ด้วยการทำให้แน่ใจว่าไม่มีตัวอักษรอีกต่อไปstringstreamหลังจากการแปลง:

bool str2int (int &i, char const *s)
{
    char              c;
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail() || ss.get(c)) {
        // not an integer
        return false;
    }
    return true;
}

เราแก้ไขปัญหาหนึ่ง แต่ยังมีปัญหาอื่นอีกสองสามปัญหา

เกิดอะไรขึ้นถ้าตัวเลขในสตริงไม่เป็นฐาน 10? เราสามารถพยายามที่จะรองรับฐานอื่น ๆ โดยการตั้งค่าสตรีมไปยังโหมดที่ถูกต้อง (เช่นss << std::hex) ก่อนที่จะลองการแปลง แต่นี่หมายความว่าผู้โทรจะต้องรู้เบื้องต้นว่าหมายเลขนั้นคืออะไร - และผู้โทรจะรู้ได้อย่างไร? ผู้โทรไม่ทราบว่าหมายเลขนั้นคืออะไร พวกเขาไม่รู้ด้วยซ้ำว่าเป็นหมายเลข! พวกเขาจะคาดหวังได้อย่างไรว่าจะรู้ว่าฐานคืออะไร เราสามารถสั่งให้ตัวเลขทั้งหมดที่ป้อนเข้าสู่โปรแกรมของเราต้องเป็นฐาน 10 และปฏิเสธอินพุตฐานสิบหกหรือฐานแปดไม่ถูกต้อง แต่นั่นไม่ใช่ความยืดหยุ่นหรือความแข็งแกร่ง ไม่มีวิธีแก้ไขปัญหานี้อย่างง่าย คุณไม่สามารถลองใช้การแปลงเพียงครั้งเดียวสำหรับแต่ละฐานได้เนื่องจากการแปลงเลขฐานสิบจะประสบความสำเร็จเสมอสำหรับตัวเลขฐานแปด (ที่มีศูนย์นำหน้า) และการแปลงฐานแปดอาจประสบความสำเร็จสำหรับตัวเลขทศนิยมบางตัว ดังนั้นตอนนี้คุณต้องตรวจสอบศูนย์นำ แต่เดี๋ยวก่อน! เลขฐานสิบหกสามารถเริ่มต้นด้วยศูนย์นำหน้าด้วย (0x ... ) ถอนหายใจ

แม้ว่าคุณจะประสบความสำเร็จในการจัดการกับปัญหาข้างต้น แต่ก็ยังมีอีกปัญหาที่ใหญ่กว่า: ถ้าผู้เรียกต้องแยกแยะระหว่างอินพุตที่ไม่ดี (เช่น "123foo") และตัวเลขที่อยู่นอกช่วงint(เช่น "4000000000" สำหรับ 32 บิตint) ด้วยstringstreamไม่มีทางที่จะทำให้ความแตกต่างนี้ เรารู้ว่าการแปลงสำเร็จหรือล้มเหลว ถ้ามันล้มเหลวเราไม่มีทางรู้ว่าทำไมมันถึงล้มเหลว อย่างที่คุณสามารถเห็นstringstreamได้ว่าต้องการมากถ้าคุณต้องการความทนทานและการจัดการข้อผิดพลาดที่ชัดเจน

นำไปสู่การนี้ฉันไปชิ้นที่สองของฉันของคำแนะนำ: ทำไม่ใช้เพิ่มเป็นlexical_castสำหรับการนี้ พิจารณาlexical_castเอกสารที่จะพูด:

ในกรณีที่ต้องการการควบคุมในระดับที่สูงกว่าสำหรับการแปลง std :: stringstream และ std :: wstringstream ให้เส้นทางที่เหมาะสมกว่า ในกรณีที่จำเป็นต้องใช้การแปลงที่ไม่ใช่แบบสตรีม lexical_cast เป็นเครื่องมือที่ผิดสำหรับงานและไม่ได้เป็นกรณีพิเศษสำหรับสถานการณ์ดังกล่าว

อะไร?? เราได้เห็นแล้วว่าstringstreamมีระดับการควบคุมที่ไม่ดี แต่มันก็บอกว่าstringstreamควรใช้แทนlexical_castถ้าคุณต้องการ "การควบคุมระดับที่สูงขึ้น" นอกจากนี้เนื่องจากlexical_castเป็นเพียงเครื่องห่อหุ้มรอบ ๆstringstreamมันได้รับความทุกข์ทรมานจากปัญหาเดียวกันที่เกิดขึ้นstringstream: การสนับสนุนที่ไม่ดีสำหรับหลายฐานจำนวนและการจัดการข้อผิดพลาดที่ไม่ดี

ทางออกที่ดีที่สุด

โชคดีที่มีคนแก้ปัญหาข้างต้นทั้งหมดแล้ว ไลบรารีมาตรฐาน C ประกอบด้วยstrtolและตระกูลที่ไม่มีปัญหาเหล่านี้

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
    char *end;
    long  l;
    errno = 0;
    l = strtol(s, &end, base);
    if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
        return OVERFLOW;
    }
    if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
        return UNDERFLOW;
    }
    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }
    i = l;
    return SUCCESS;
}

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

เหตุผลอื่นที่ทำให้ชอบstrtol(และครอบครัว):

  • มันแสดงประสิทธิภาพรันไทม์ที่ดีขึ้นมาก
  • มันแนะนำค่าใช้จ่ายในการคอมไพล์เวลาน้อยกว่า (ส่วนอื่นดึง SLOC มากกว่า 20 เท่าจากส่วนหัว)
  • มันส่งผลให้ขนาดรหัสที่เล็กที่สุด

ไม่มีเหตุผลที่ดีที่จะใช้วิธีอื่นใด


22
@JamesDunne: POSIX ต้องstrtolมีความปลอดภัยต่อเธรด POSIX ยังต้องการerrnoใช้หน่วยเก็บข้อมูลเธรดโลคัล แม้แต่ในระบบที่ไม่ใช่ POSIX การใช้งานเกือบทั้งหมดของerrnoระบบแบบมัลติเธรดใช้หน่วยเก็บข้อมูลเธรดโลคัล มาตรฐาน C ++ ล่าสุดจำเป็นต้องerrnoสอดคล้องกับ POSIX มาตรฐาน C ล่าสุดยังต้องerrnoมีหน่วยเก็บข้อมูลเธรดโลคัล แม้กระทั่งบน Windows ซึ่งไม่สอดคล้องกับ POSIXerrnoมีความปลอดภัยของเธรดและโดยส่วนขยายดังนั้นจึงเป็นstrtolเช่นนั้น
Dan Molding

7
ฉันไม่สามารถติดตามเหตุผลของคุณกับการใช้การเพิ่ม :: lexical_cast ดังที่พวกเขากล่าวว่า std :: stringstream ทำให้มีการควบคุมมากมาย - คุณทำทุกอย่างตั้งแต่การตรวจสอบข้อผิดพลาดไปจนถึงการกำหนดฐานของตัวเอง เอกสารปัจจุบันทำให้เป็นดังนี้: "สำหรับการแปลงที่เกี่ยวข้องมากขึ้นเช่นที่ความแม่นยำหรือการจัดรูปแบบต้องการการควบคุมที่เข้มงวดกว่าที่เสนอโดยพฤติกรรมเริ่มต้นของ lexical_cast แนะนำให้ใช้แนวทาง std :: stringstream ทั่วไป"
fhd

8
นี่คือการเข้ารหัส C ที่ไม่เหมาะสมภายใน C ++ ไลบรารีมาตรฐานประกอบด้วยstd::stolสิ่งนี้ซึ่งจะโยนข้อยกเว้นได้อย่างเหมาะสมแทนที่จะส่งคืนค่าคงที่
fuzzyTew

22
@fuzzyTew ฉันเขียนคำตอบนี้ก่อนที่จะstd::stolถูกเพิ่มไปยังภาษา C ++ ที่กล่าวว่าฉันไม่คิดว่ามันยุติธรรมที่จะบอกว่านี่คือ "การเข้ารหัส C ภายใน C ++" มันโง่ที่จะบอกว่าstd::strtolเป็นรหัส C เมื่อมันเป็นส่วนหนึ่งของภาษา C ++ อย่างชัดเจน คำตอบของฉันนำมาใช้อย่างสมบูรณ์แบบกับ C ++ std::stolเมื่อมันถูกเขียนและยังไม่ใช้แม้จะมีใหม่ ฟังก์ชั่นการโทรที่อาจส่งข้อยกเว้นไม่ได้ดีที่สุดสำหรับทุกสถานการณ์การเขียนโปรแกรม
Dan Moulding

9
@fuzzyTew: พื้นที่ดิสก์ไม่เพียงพอเป็นเงื่อนไขที่ยอดเยี่ยม ไฟล์ข้อมูลที่มีรูปแบบไม่ถูกต้องซึ่งผลิตโดยคอมพิวเตอร์เป็นเงื่อนไขที่ยอดเยี่ยม แต่ความผิดพลาดในการป้อนข้อมูลของผู้ใช้ไม่ได้ยอดเยี่ยม มันเป็นการดีที่จะมีวิธีการแยกวิเคราะห์ซึ่งสามารถจัดการกับความล้มเหลวในการแยกวิเคราะห์แบบปกติและไม่พิเศษ
Ben Voigt

67

นี่เป็นวิธี C ที่ปลอดภัยกว่า atoi ()

const char* str = "123";
int i;

if(sscanf(str, "%d", &i)  == EOF )
{
   /* error */
}

C ++ พร้อมกับไลบรารี่มาตรฐาน: (ขอบคุณCMS )

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR 
  }
  return num;
}

ด้วยห้องสมุดเพิ่ม : (ขอบคุณjk )

#include <boost/lexical_cast.hpp>
#include <string>

try
{
    std::string str = "123";
    int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
    // Error
}

แก้ไข: แก้ไขเวอร์ชันสตริงสตรีมเพื่อจัดการข้อผิดพลาด (ขอบคุณที่ CMS และ jk แสดงความคิดเห็นในโพสต์ต้นฉบับ)


1
โปรดอัปเดตเวอร์ชันสตริงสตรีมของคุณเพื่อรวมการตรวจสอบสตริงสตรีม :: ล้มเหลว () (ตามที่ผู้ร้องขอร้องขอ "การจัดการข้อผิดพลาดที่มีประสิทธิภาพและชัดเจน")
jk

2
รุ่นสตริงสตรีมของคุณจะยอมรับสิ่งต่างๆเช่น "10haha" โดยไม่ต้องบ่น
Johannes Schaub - litb

3
เปลี่ยนเป็น (! (ss >> num) .fail () && (ss >> ws) .eof ()) จาก ((ss >> num) .fail ()) หากคุณต้องการการจัดการแบบเดียวกันเช่น lexical_cast
Johannes Schaub - litb

3
C ++ ที่มีเมธอด stringstream ไลบรารี่มาตรฐานไม่สามารถใช้ได้กับสตริงเช่น "12-SomeString" แม้จะมีการตรวจสอบ. fail ()
captonssj

C ++ 11 มีฟังก์ชั่นมาตรฐานที่รวดเร็วสำหรับตอนนี้
fuzzyTew

21

วิธี C เก่าที่ยังคงใช้งานได้ดี ฉันแนะนำ strtol หรือ strtoul ระหว่างสถานะการส่งคืนและ 'endPtr' คุณสามารถให้ผลลัพธ์การวิเคราะห์ที่ดีได้ นอกจากนี้ยังจัดการฐานหลายอย่าง


4
โอ้โปรดอย่าใช้สิ่ง C เก่านี้เมื่อเขียนโปรแกรม C ++ มีวิธีที่ดีกว่า / ง่ายขึ้น / สะอาดขึ้น / ทันสมัยขึ้น / ปลอดภัยกว่าใน C ++!
jk

27
เป็นเรื่องตลกเมื่อผู้คนมีความกังวลเกี่ยวกับวิธีการที่ทันสมัยกว่าในการแก้ปัญหา
J Miller

@Jason, IMO ที่ปลอดภัยกว่าและการจัดการข้อผิดพลาดเป็นแนวคิดที่ทันสมัยกว่าเมื่อเทียบกับ C
Eugene Yokota

6
ฉันได้ดูคำตอบอื่น ๆ แล้วและก็ไม่มีอะไรจะดีกว่า / ง่ายขึ้น / สะอาดขึ้นหรือปลอดภัยขึ้น โปสเตอร์บอกว่าเขามีถ่าน * นั่น จำกัด ปริมาณความปลอดภัยที่คุณจะได้รับ :)
Chris Arguin

21

คุณสามารถใช้Boost'slexical_castซึ่งล้อมรอบสิ่งนี้ในอินเทอร์เฟซทั่วไปที่มากกว่า lexical_cast<Target>(Source)พ่นbad_lexical_castความล้มเหลว


12
เพิ่ม lexical_cast ช้ามากและไม่มีประสิทธิภาพอย่างเจ็บปวด

3
@Matthieu การปรับปรุงเพื่อเพิ่มได้ทำให้ประสิทธิภาพที่เพิ่มขึ้นไม่น้อย: boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/... (ดูstackoverflow.com/questions/1250795/... )
บิน

16

คุณสามารถใช้สตริงสตรีมจากบรรณารักษ์มาตรฐาน C ++:

stringstream ss(str);
int x;
ss >> x;

if(ss) { // <-- error handling
  // use x
} else {
  // not a number
}

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

ดูสตรีมข้อผิดพลาดสำหรับข้อผิดพลาดของการจัดการข้อผิดพลาดและสตรีมใน C ++


2
วิธีการแบบสตริง C ++ ไม่สามารถใช้งานกับสตริงเช่น "12-SomeString" แม้จะมีการตรวจสอบ 'กระแสสถานะ'
captonssj

10

คุณสามารถใช้สตริงสตรีม

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}

4
แต่นี่ไม่ได้จัดการข้อผิดพลาดใด ๆ คุณต้องตรวจสอบความล้มเหลวของสตรีม
jk

1
ถูกต้องคุณต้องตรวจสอบกระแสหาก ((ss >> num) .fail ()) {// ERROR}
CMS

2
วิธีการแบบสตริง C ++ ไม่สามารถใช้งานกับสตริงเช่น "12-SomeString" แม้จะมีการตรวจสอบ 'กระแสสถานะ'
captonssj

8

ฉันคิดว่าลิงก์ทั้งสามนี้สรุป:

วิธีการแก้ปัญหาแบบสตริงสตรีมและ lexical_cast มีความคล้ายคลึงกับ lexical cast กำลังใช้ stringstream

ความเชี่ยวชาญเฉพาะด้านของคำศัพท์ cast ใช้วิธีที่ต่างกันดูhttp://www.boost.org/doc/libs/release/boost/lexical_cast.hppสำหรับรายละเอียด ตอนนี้จำนวนเต็มและลอยเป็นพิเศษสำหรับการแปลงจำนวนเต็มเพื่อสตริง

หนึ่งสามารถเชี่ยวชาญ lexical_cast สำหรับความต้องการของตนเองและทำให้มันรวดเร็ว นี่จะเป็นทางออกที่ดีที่สุดที่ทำให้ทุกฝ่ายสะอาดและเรียบง่าย

บทความที่กล่าวถึงแล้วแสดงการเปรียบเทียบระหว่างวิธีการแปลงสตริงจำนวนเต็ม <-> วิธีการดังต่อไปนี้เหมาะสม: c-way เก่า, Spirit.karma, fastformat, loop ที่ไร้เดียงสาอย่างง่าย

Lexical_cast นั้นใช้ได้ในบางกรณีเช่นสำหรับการแปลงสตริงเป็น int

การแปลงสตริงเป็น int โดยใช้ lexical cast ไม่ใช่ความคิดที่ดีเพราะมันช้ากว่า atoi 10-40 เท่าขึ้นอยู่กับแพลตฟอร์ม / คอมไพเลอร์ที่ใช้

Boost.Spirit.Karma น่าจะเป็นห้องสมุดที่เร็วที่สุดสำหรับการแปลงจำนวนเต็มเป็นสตริง

ex.: generate(ptr_char, int_, integer_number);

และลูปพื้นฐานแบบง่าย ๆ จากบทความที่กล่าวถึงข้างต้นเป็นวิธีที่เร็วที่สุดในการแปลงสตริงเป็น int ซึ่งไม่ใช่วิธีที่ปลอดภัยที่สุด strtol () ดูเหมือนว่าเป็นวิธีที่ปลอดภัยกว่า

int naive_char_2_int(const char *p) {
    int x = 0;
    bool neg = false;
    if (*p == '-') {
        neg = true;
        ++p;
    }
    while (*p >= '0' && *p <= '9') {
        x = (x*10) + (*p - '0');
        ++p;
    }
    if (neg) {
        x = -x;
    }
    return x;
}

7

c ++ String Toolkit ห้องสมุด (StrTk)มีวิธีการแก้ปัญหาต่อไปนี้:

static const std::size_t digit_table_symbol_count = 256;
static const unsigned char digit_table[digit_table_symbol_count] = {
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 - 0x0F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 - 0x17
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 - 0x1F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 - 0x27
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x28 - 0x2F
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
   0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 - 0x3F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 - 0x47
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x48 - 0x4F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 - 0x57
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 - 0x5F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 - 0x67
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x68 - 0x6F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x70 - 0x77
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 - 0x7F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x80 - 0x87
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x88 - 0x8F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x90 - 0x97
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x98 - 0x9F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA0 - 0xA7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA8 - 0xAF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB0 - 0xB7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB8 - 0xBF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC0 - 0xC7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC8 - 0xCF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD0 - 0xD7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD8 - 0xDF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 - 0xE7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE8 - 0xEF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xF0 - 0xF7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 0xF8 - 0xFF
 };

template<typename InputIterator, typename T>
inline bool string_to_signed_type_converter_impl_itr(InputIterator begin, InputIterator end, T& v)
{
   if (0 == std::distance(begin,end))
      return false;
   v = 0;
   InputIterator it = begin;
   bool negative = false;
   if ('+' == *it)
      ++it;
   else if ('-' == *it)
   {
      ++it;
      negative = true;
   }
   if (end == it)
      return false;
   while(end != it)
   {
      const T digit = static_cast<T>(digit_table[static_cast<unsigned int>(*it++)]);
      if (0xFF == digit)
         return false;
      v = (10 * v) + digit;
   }
   if (negative)
      v *= -1;
   return true;
}

InputIterator สามารถเป็นได้ทั้ง char *, char * หรือ std :: string iterators และ T คาดว่าจะเป็น int ลงนามเช่น int ลงนาม, int หรือยาว


1
คำเตือนการติดตั้งใช้งานนี้ดูดี แต่ไม่รองรับการโอเวอร์โฟลว์เท่าที่ฉันจะบอกได้
Vinnie Falco

2
รหัสไม่รองรับการล้น ล้นไม่จำเป็นต้องมีการป้อนข้อมูลสตริงที่มีค่าข้อความของv = (10 * v) + digit; INT_MINตารางมีค่าที่น่าสงสัย vs เพียงแค่digit >= '0' && digit <= '9'
chux - Reinstate Monica

6

หากคุณมี C ++ 11 การแก้ปัญหาที่เหมาะสมในปัจจุบันเป็นจำนวนเต็ม c ++ ฟังก์ชั่นแปลงใน<string>: stoi, stol, stoul, ,stoll stoullพวกเขาโยนข้อยกเว้นที่เหมาะสมเมื่อได้รับการป้อนข้อมูลที่ไม่ถูกต้องและใช้เร็วและขนาดเล็กstrto*ฟังก์ชั่นที่ภายใต้ประทุน

หากคุณติดอยู่กับการแก้ไข C ++ ก่อนหน้านี้มันจะเป็นแบบพกพาของคุณที่จะเลียนแบบฟังก์ชั่นเหล่านี้ในการใช้งานของคุณ


6

ตั้งแต่ C ++ 17 เป็นต้นไปคุณสามารถใช้std::from_charsจาก<charconv>ส่วนหัวดังที่ระบุไว้ที่นี่ที่นี่

ตัวอย่างเช่น:

#include <iostream>
#include <charconv>
#include <array>

int main()
{
    char const * str = "42";
    int value = 0;

    std::from_chars_result result = std::from_chars(std::begin(str), std::end(str), value);

    if(result.error == std::errc::invalid_argument)
    {
      std::cout << "Error, invalid format";
    }
    else if(result.error == std::errc::result_out_of_range)
    {
      std::cout << "Error, value too big for int range";
    }
    else
    {
      std::cout << "Success: " << result;
    }
}

โบนัสสามารถจัดการกับฐานอื่น ๆ เช่นเลขฐานสิบหก


3

ฉันชอบคำตอบของ Dan Mouldingฉันจะเพิ่มสไตล์ C ++ นิดหน่อย:

#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

int to_int(const std::string &s, int base = 0)
{
    char *end;
    errno = 0;
    long result = std::strtol(s.c_str(), &end, base);
    if (errno == ERANGE || result > INT_MAX || result < INT_MIN)
        throw std::out_of_range("toint: string is out of range");
    if (s.length() == 0 || *end != '\0')
        throw std::invalid_argument("toint: invalid string");
    return result;
}

มันทำงานได้ทั้ง std :: string และ const char * ผ่านการแปลงโดยนัย นอกจากนี้ยังมีประโยชน์สำหรับการแปลงฐานเช่นทั้งหมดto_int("0x7b")และto_int("0173")และto_int("01111011", 2)และto_int("0000007B", 16)และto_int("11120", 3)และto_int("3L", 34);จะส่งคืน 123

ไม่เหมือนstd::stoiใน pre-C ++ 11 ยังไม่เหมือนstd::stoiกันboost::lexical_castและstringstreamมันก็มีข้อยกเว้นสำหรับสตริงแปลก ๆ เช่น "123hohoho"

หมายเหตุ: ฟังก์ชั่นนี้จะยอมให้มีช่องว่างนำหน้า แต่ไม่ใช่ช่องว่างต่อท้ายเช่นto_int(" 123")ส่งคืน 123 ขณะที่ส่งto_int("123 ")ข้อยกเว้น ตรวจสอบให้แน่ใจว่านี่เป็นที่ยอมรับได้สำหรับกรณีการใช้งานหรือปรับรหัส

ฟังก์ชั่นดังกล่าวอาจเป็นส่วนหนึ่งของ STL ...


2

ฉันรู้สามวิธีในการแปลง String ให้เป็น int:

ไม่ว่าจะใช้ฟังก์ชั่น stoi (String to int) หรือเพียงแค่ไปกับ Stringstream วิธีที่สามในการแปลงเป็นรายบุคคลโค้ดอยู่ด้านล่าง:

วิธีที่ 1

std::string s1 = "4533";
std::string s2 = "3.010101";
std::string s3 = "31337 with some string";

int myint1 = std::stoi(s1);
int myint2 = std::stoi(s2);
int myint3 = std::stoi(s3);

std::cout <<  s1 <<"=" << myint1 << '\n';
std::cout <<  s2 <<"=" << myint2 << '\n';
std::cout <<  s3 <<"=" << myint3 << '\n';

วิธีที่ 2

#include <string.h>
#include <sstream>
#include <iostream>
#include <cstring>
using namespace std;


int StringToInteger(string NumberAsString)
{
    int NumberAsInteger;
    stringstream ss;
    ss << NumberAsString;
    ss >> NumberAsInteger;
    return NumberAsInteger;
}
int main()
{
    string NumberAsString;
    cin >> NumberAsString;
    cout << StringToInteger(NumberAsString) << endl;
    return 0;
} 

วิธีที่ 3 - แต่ไม่ใช่สำหรับการแปลงเดี่ยว

std::string str4 = "453";
int i = 0, in=0; // 453 as on
for ( i = 0; i < str4.length(); i++)
{

    in = str4[i];
    cout <<in-48 ;

}

1

ฉันชอบคำตอบของแดนโดยเฉพาะเพราะหลีกเลี่ยงข้อยกเว้น สำหรับการพัฒนาระบบฝังตัวและการพัฒนาระบบระดับต่ำอื่น ๆ อาจไม่มีกรอบข้อยกเว้นที่เหมาะสม

เพิ่มการตรวจสอบ white-space หลังจากสตริงที่ถูกต้อง ... ทั้งสามบรรทัด

    while (isspace(*end)) {
        end++;
    }


เพิ่มการตรวจสอบการแยกวิเคราะห์ข้อผิดพลาดด้วย

    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }


นี่คือฟังก์ชั่นที่สมบูรณ์ ..

#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2long (long &l, char const *s, int base = 0)
{
    char *end = (char *)s;
    errno = 0;

    l = strtol(s, &end, base);

    if ((errno == ERANGE) && (l == LONG_MAX)) {
        return OVERFLOW;
    }
    if ((errno == ERANGE) && (l == LONG_MIN)) {
        return UNDERFLOW;
    }
    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }    
    while (isspace((unsigned char)*end)) {
        end++;
    }

    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }

    return SUCCESS;
}

@chux เพิ่มรหัสเพื่อดูแลข้อกังวลที่คุณกล่าวถึง
pellucide

1) " "ยังคงล้มเหลวในการตรวจสอบข้อผิดพลาดด้วยการป้อนข้อมูลเช่น strtol()ไม่ได้ระบุไว้เพื่อตั้งค่าerrnoเมื่อไม่มีการแปลงเกิดขึ้น ดีกว่าที่จะใช้if (s == end) return INCONVERTIBLE; ในการตรวจสอบไม่มีการแปลง จากนั้น if (*s == '\0' || *end != '\0')สามารถลดความซับซ้อนลงเหลือif (*end)2) || l > LONG_MAXและ|| l < LONG_MINไม่ให้บริการตามวัตถุประสงค์ - ไม่เป็นความจริง
chux - Reinstate Monica

@chux บน mac errno ถูกตั้งค่าสำหรับการแยกวิเคราะห์ข้อผิดพลาด แต่ใน linux errno ไม่ได้ตั้งค่า เปลี่ยนรหัสเพื่อขึ้นอยู่กับตัวชี้ "จบ" เพื่อตรวจสอบว่า
pellucide

0

คุณสามารถใช้วิธีนี้ได้

#define toInt(x) {atoi(x.c_str())};

และถ้าคุณต้องแปลงจาก String เป็น Integer คุณก็แค่ทำสิ่งต่อไปนี้

int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

ผลลัพธ์จะเป็น 102


4
idk เขียนกำหนดรอบแมโครatoiไม่ได้ดูเหมือน "ซี ++ วิธี" ในแง่ของคำตอบอื่น ๆ std::stoi()เช่นการได้รับการยอมรับ
Eugene Yokota

ฉันพบว่ามันสนุกมากขึ้นโดยใช้วิธีที่กำหนดไว้ล่วงหน้า: P
Boris

0

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

  • สามารถแปลงฐานใด ๆ (และตรวจจับประเภทฐาน)
  • จะตรวจจับข้อมูลที่ผิดพลาด (เช่นตรวจสอบให้แน่ใจว่าสตริงทั้งหมด, ช่องว่างนำหน้า / ต่อท้ายน้อยกว่า, ถูกใช้โดยการแปลง)
  • จะตรวจสอบให้แน่ใจว่าไม่ว่าจะแปลงเป็นประเภทใดช่วงของค่าสตริงจะเป็นที่ยอมรับ

ดังนั้นนี่คือของฉันพร้อมสายทดสอบ เพราะมันใช้ฟังก์ชั่น C strtoull / strtoll ใต้ฝากระโปรงมันจึงแปลงก่อนเป็นประเภทที่ใหญ่ที่สุดที่มีอยู่เสมอ จากนั้นหากคุณไม่ได้ใช้ประเภทที่ใหญ่ที่สุดมันจะทำการตรวจสอบช่วงเพิ่มเติมเพื่อตรวจสอบประเภทของคุณที่ไม่ได้ไหล (ใต้) สำหรับเรื่องนี้มันเป็นนักแสดงน้อยกว่าถ้าใครเลือก strtol / strtoul อย่างไรก็ตามมันใช้งานได้กับกางเกงขาสั้น / ตัวอักษรและเพื่อความรู้ที่ดีที่สุดของฉันไม่มีฟังก์ชั่นห้องสมุดมาตรฐานที่ทำเช่นนั้น

สนุก; หวังว่าใครบางคนพบว่ามันมีประโยชน์

#include <cstdlib>
#include <cerrno>
#include <limits>
#include <stdexcept>
#include <sstream>

static const int DefaultBase = 10;

template<typename T>
static inline T CstrtoxllWrapper(const char *str, int base = DefaultBase)
{
    while (isspace(*str)) str++; // remove leading spaces; verify there's data
    if (*str == '\0') { throw std::invalid_argument("str; no data"); } // nothing to convert

    // NOTE:  for some reason strtoull allows a negative sign, we don't; if
    //          converting to an unsigned then it must always be positive!
    if (!std::numeric_limits<T>::is_signed && *str == '-')
    { throw std::invalid_argument("str; negative"); }

    // reset errno and call fn (either strtoll or strtoull)
    errno = 0;
    char *ePtr;
    T tmp = std::numeric_limits<T>::is_signed ? strtoll(str, &ePtr, base)
                                              : strtoull(str, &ePtr, base);

    // check for any C errors -- note these are range errors on T, which may
    //   still be out of the range of the actual type we're using; the caller
    //   may need to perform additional range checks.
    if (errno != 0) 
    {
            if (errno == ERANGE) { throw std::range_error("str; out of range"); }
            else if (errno == EINVAL) { throw std::invalid_argument("str; EINVAL"); }
            else { throw std::invalid_argument("str; unknown errno"); }
    }

    // verify everything converted -- extraneous spaces are allowed
    if (ePtr != NULL)
    {
            while (isspace(*ePtr)) ePtr++;
            if (*ePtr != '\0') { throw std::invalid_argument("str; bad data"); }
    }

    return tmp;
}

template<typename T>
T StringToSigned(const char *str, int base = DefaultBase)
{
    static const long long max = std::numeric_limits<T>::max();
    static const long long min = std::numeric_limits<T>::min();

    long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp < min || tmp > max)
    {
            std::ostringstream err;
            err << "str; value " << tmp << " out of " << sizeof(T) * 8
                << "-bit signed range (";
            if (sizeof(T) != 1) err << min << ".." << max;
            else err << (int) min << ".." << (int) max;  // don't print garbage chars
            err << ")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
T StringToUnsigned(const char *str, int base = DefaultBase)
{
    static const unsigned long long max = std::numeric_limits<T>::max();

    unsigned long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp > max)
    {
            std::ostringstream err;
            err << "str; value " << tmp << " out of " << sizeof(T) * 8
                << "-bit unsigned range (0..";
            if (sizeof(T) != 1) err << max;
            else err << (int) max;  // don't print garbage chars
            err << ")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
inline T
StringToDecimal(const char *str, int base = DefaultBase)
{
    return std::numeric_limits<T>::is_signed ? StringToSigned<T>(str, base)
                                             : StringToUnsigned<T>(str, base);
}

template<typename T>
inline T
StringToDecimal(T &out_convertedVal, const char *str, int base = DefaultBase)
{
    return out_convertedVal = StringToDecimal<T>(str, base);
}

/*============================== [ Test Strap ] ==============================*/ 

#include <inttypes.h>
#include <iostream>

static bool _g_anyFailed = false;

template<typename T>
void TestIt(const char *tName,
            const char *s, int base,
            bool successExpected = false, T expectedValue = 0)
{
    #define FAIL(s) { _g_anyFailed = true; std::cout << s; }

    T x;
    std::cout << "converting<" << tName << ">b:" << base << " [" << s << "]";
    try
    {
            StringToDecimal<T>(x, s, base);
            // get here on success only
            if (!successExpected)
            {
                    FAIL(" -- TEST FAILED; SUCCESS NOT EXPECTED!" << std::endl);
            }
            else
            {
                    std::cout << " -> ";
                    if (sizeof(T) != 1) std::cout << x;
                    else std::cout << (int) x;  // don't print garbage chars
                    if (x != expectedValue)
                    {
                            FAIL("; FAILED (expected value:" << expectedValue << ")!");
                    }
                    std::cout << std::endl;
            }
    }
    catch (std::exception &e)
    {
            if (successExpected)
            {
                    FAIL(   " -- TEST FAILED; EXPECTED SUCCESS!"
                         << " (got:" << e.what() << ")" << std::endl);
            }
            else
            {
                    std::cout << "; expected exception encounterd: [" << e.what() << "]" << std::endl;
            }
    }
}

#define TEST(t, s, ...) \
    TestIt<t>(#t, s, __VA_ARGS__);

int main()
{
    std::cout << "============ variable base tests ============" << std::endl;
    TEST(int, "-0xF", 0, true, -0xF);
    TEST(int, "+0xF", 0, true, 0xF);
    TEST(int, "0xF", 0, true, 0xF);
    TEST(int, "-010", 0, true, -010);
    TEST(int, "+010", 0, true, 010);
    TEST(int, "010", 0, true, 010);
    TEST(int, "-10", 0, true, -10);
    TEST(int, "+10", 0, true, 10);
    TEST(int, "10", 0, true, 10);

    std::cout << "============ base-10 tests ============" << std::endl;
    TEST(int, "-010", 10, true, -10);
    TEST(int, "+010", 10, true, 10);
    TEST(int, "010", 10, true, 10);
    TEST(int, "-10", 10, true, -10);
    TEST(int, "+10", 10, true, 10);
    TEST(int, "10", 10, true, 10);
    TEST(int, "00010", 10, true, 10);

    std::cout << "============ base-8 tests ============" << std::endl;
    TEST(int, "777", 8, true, 0777);
    TEST(int, "-0111 ", 8, true, -0111);
    TEST(int, "+0010 ", 8, true, 010);

    std::cout << "============ base-16 tests ============" << std::endl;
    TEST(int, "DEAD", 16, true, 0xDEAD);
    TEST(int, "-BEEF", 16, true, -0xBEEF);
    TEST(int, "+C30", 16, true, 0xC30);

    std::cout << "============ base-2 tests ============" << std::endl;
    TEST(int, "-10011001", 2, true, -153);
    TEST(int, "10011001", 2, true, 153);

    std::cout << "============ irregular base tests ============" << std::endl;
    TEST(int, "Z", 36, true, 35);
    TEST(int, "ZZTOP", 36, true, 60457993);
    TEST(int, "G", 17, true, 16);
    TEST(int, "H", 17);

    std::cout << "============ space deliminated tests ============" << std::endl;
    TEST(int, "1337    ", 10, true, 1337);
    TEST(int, "   FEAD", 16, true, 0xFEAD);
    TEST(int, "   0711   ", 0, true, 0711);

    std::cout << "============ bad data tests ============" << std::endl;
    TEST(int, "FEAD", 10);
    TEST(int, "1234 asdfklj", 10);
    TEST(int, "-0xF", 10);
    TEST(int, "+0xF", 10);
    TEST(int, "0xF", 10);
    TEST(int, "-F", 10);
    TEST(int, "+F", 10);
    TEST(int, "12.4", 10);
    TEST(int, "ABG", 16);
    TEST(int, "10011002", 2);

    std::cout << "============ int8_t range tests ============" << std::endl;
    TEST(int8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(int8_t, "80", 16);
    TEST(int8_t, "-80", 16, true, std::numeric_limits<int8_t>::min());
    TEST(int8_t, "-81", 16);
    TEST(int8_t, "FF", 16);
    TEST(int8_t, "100", 16);

    std::cout << "============ uint8_t range tests ============" << std::endl;
    TEST(uint8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(uint8_t, "80", 16, true, std::numeric_limits<int8_t>::max()+1);
    TEST(uint8_t, "-80", 16);
    TEST(uint8_t, "-81", 16);
    TEST(uint8_t, "FF", 16, true, std::numeric_limits<uint8_t>::max());
    TEST(uint8_t, "100", 16);

    std::cout << "============ int16_t range tests ============" << std::endl;
    TEST(int16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(int16_t, "8000", 16);
    TEST(int16_t, "-8000", 16, true, std::numeric_limits<int16_t>::min());
    TEST(int16_t, "-8001", 16);
    TEST(int16_t, "FFFF", 16);
    TEST(int16_t, "10000", 16);

    std::cout << "============ uint16_t range tests ============" << std::endl;
    TEST(uint16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(uint16_t, "8000", 16, true, std::numeric_limits<int16_t>::max()+1);
    TEST(uint16_t, "-8000", 16);
    TEST(uint16_t, "-8001", 16);
    TEST(uint16_t, "FFFF", 16, true, std::numeric_limits<uint16_t>::max());
    TEST(uint16_t, "10000", 16);

    std::cout << "============ int32_t range tests ============" << std::endl;
    TEST(int32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(int32_t, "80000000", 16);
    TEST(int32_t, "-80000000", 16, true, std::numeric_limits<int32_t>::min());
    TEST(int32_t, "-80000001", 16);
    TEST(int32_t, "FFFFFFFF", 16);
    TEST(int32_t, "100000000", 16);

    std::cout << "============ uint32_t range tests ============" << std::endl;
    TEST(uint32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(uint32_t, "80000000", 16, true, std::numeric_limits<int32_t>::max()+1);
    TEST(uint32_t, "-80000000", 16);
    TEST(uint32_t, "-80000001", 16);
    TEST(uint32_t, "FFFFFFFF", 16, true, std::numeric_limits<uint32_t>::max());
    TEST(uint32_t, "100000000", 16);

    std::cout << "============ int64_t range tests ============" << std::endl;
    TEST(int64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(int64_t, "8000000000000000", 16);
    TEST(int64_t, "-8000000000000000", 16, true, std::numeric_limits<int64_t>::min());
    TEST(int64_t, "-8000000000000001", 16);
    TEST(int64_t, "FFFFFFFFFFFFFFFF", 16);
    TEST(int64_t, "10000000000000000", 16);

    std::cout << "============ uint64_t range tests ============" << std::endl;
    TEST(uint64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(uint64_t, "8000000000000000", 16, true, std::numeric_limits<int64_t>::max()+1);
    TEST(uint64_t, "-8000000000000000", 16);
    TEST(uint64_t, "-8000000000000001", 16);
    TEST(uint64_t, "FFFFFFFFFFFFFFFF", 16, true, std::numeric_limits<uint64_t>::max());
    TEST(uint64_t, "10000000000000000", 16);

    std::cout << std::endl << std::endl
              << (_g_anyFailed ? "!! SOME TESTS FAILED !!" : "ALL TESTS PASSED")
              << std::endl;

    return _g_anyFailed;
}

StringToDecimalเป็นวิธีการที่ผู้ใช้ที่ดิน; มันโอเวอร์โหลดดังนั้นจึงสามารถเรียกได้ว่าเป็นแบบนี้:

int a; a = StringToDecimal<int>("100");

หรือสิ่งนี้:

int a; StringToDecimal(a, "100");

ฉันเกลียดการซ้ำประเภท int ดังนั้นควรเลือกประเภทหลัง วิธีนี้ช่วยให้มั่นใจได้ว่าหากการเปลี่ยนแปลงประเภท 'a' ไม่ได้ผลลัพธ์ที่ไม่ดี ฉันหวังว่าคอมไพเลอร์สามารถคิดออกเช่น:

int a; a = StringToDecimal("100");

... แต่ C ++ ไม่ได้อนุมานประเภทการคืนค่าเทมเพลตดังนั้นฉันจึงทำได้ดีที่สุด

การติดตั้งใช้งานง่าย:

CstrtoxllWrapperล้อมทั้งstrtoullและstrtollเรียกสิ่งที่จำเป็นขึ้นอยู่กับการลงนามของประเภทแม่แบบและให้การรับประกันเพิ่มเติมบางอย่าง (เช่นการป้อนข้อมูลเชิงลบไม่ได้รับอนุญาตถ้าไม่ได้ลงนามและมันทำให้แน่ใจว่าสตริงทั้งหมดถูกแปลง)

CstrtoxllWrapperถูกใช้โดยStringToSignedและStringToUnsignedกับชนิดที่ใหญ่ที่สุด (long long / unsigned long long) ที่คอมไพเลอร์มีให้; สิ่งนี้ช่วยให้การแปลงสูงสุดที่จะดำเนินการ จากนั้นหากมีความจำเป็นStringToSigned/ StringToUnsignedดำเนินการตรวจสอบช่วงสุดท้ายในประเภทพื้นฐาน ในที่สุดวิธีการจุดสิ้นสุดStringToDecimalตัดสินใจว่าวิธีการแม่แบบ StringTo * ที่จะเรียกขึ้นอยู่กับการลงนามของประเภทพื้นฐาน

ฉันคิดว่าส่วนใหญ่ขยะสามารถปรับให้เหมาะสมโดยคอมไพเลอร์; เพียงเกี่ยวกับทุกสิ่งควรกำหนดเวลารวบรวม ความเห็นใด ๆ ในด้านนี้จะน่าสนใจสำหรับฉัน!


"ใช้ชนิดที่ใหญ่ที่สุด" -> ทำไมlong longแทนintmax_t?
chux - Reinstate Monica

if (ePtr != str)มีความมั่นใจที่คุณต้องการ นอกจากนี้การใช้งานที่จะต้องจัดการกับค่าลบของisspace((unsigned char) *ePtr) *ePtr
chux - Reinstate Monica

-3

ใน C คุณสามารถใช้int atoi (const char * str),

แยกวิเคราะห์ C-string str ตีความเนื้อหาเป็นจำนวนเต็มซึ่งถูกส่งคืนเป็นค่า type int


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