目录
1 toInt()
(1)qt_strtoll()
(2) qstrtoll()
(3)bytearrayToLongLong()
(4)toIntegral_helper
2 static QString number(int, int base=10);
(1)源码
(2)QString longLongToString() const;
(3)QString qulltoa(qulonglong l, int base, const QChar _zero);
下面是toInt()函数的内部实现源码
QString str("16");
int b = str.toInt();
//toInt
int QString::toInt(bool *ok, int base) const
{
return toIntegral_helper(constData(), size(), ok, base);
}
//toIntegral_helper
template static
T toIntegral_helper(const QChar *data, int len, bool *ok, int base)
{
// ### Qt6: use std::conditional::value, qulonglong, qlonglong>::type
const bool isUnsigned = T(0) < T(-1);
typedef typename QtPrivate::QConditional::Type Int64;
typedef typename QtPrivate::QConditional::Type Int32;
// we select the right overload by casting size() to int or uint
Int64 val = toIntegral_helper(data, Int32(len), ok, base);
if (T(val) != val) {
if (ok)
*ok = false;
val = 0;
}
return T(val);
}
//toIntegral_helper
qlonglong QString::toIntegral_helper(const QChar *data, int len, bool *ok, int base)
{
#if defined(QT_CHECK_RANGE)
if (base != 0 && (base < 2 || base > 36)) {
qWarning("QString::toULongLong: Invalid base (%d)", base);
base = 10;
}
#endif
return QLocaleData::c()->stringToLongLong(QStringView(data, len), base, ok, QLocale::RejectGroupSeparator);
}
//stringToLongLong
qlonglong QLocaleData::stringToLongLong(QStringView str, int base, bool *ok,
QLocale::NumberOptions number_options) const
{
CharBuff buff;
if (!numberToCLocale(str, number_options, &buff)) {
if (ok != nullptr)
*ok = false;
return 0;
}
return bytearrayToLongLong(buff.constData(), base, ok);
}
//bytearrayToLongLong
qlonglong QLocaleData::bytearrayToLongLong(const char *num, int base, bool *ok)
{
bool _ok;
const char *endptr;
if (*num == '\0') {
if (ok != nullptr)
*ok = false;
return 0;
}
qlonglong l = qstrtoll(num, &endptr, base, &_ok);
if (!_ok) {
if (ok != nullptr)
*ok = false;
return 0;
}
if (*endptr != '\0') {
while (ascii_isspace(*endptr))
++endptr;
}
if (*endptr != '\0') {
// we stopped at a non-digit character after converting some digits
if (ok != nullptr)
*ok = false;
return 0;
}
if (ok != nullptr)
*ok = true;
return l;
}
//qstrtoll
long long
qstrtoll(const char * nptr, const char **endptr, int base, bool *ok)
{
*ok = true;
errno = 0;
char *endptr2 = 0;
long long result = qt_strtoll(nptr, &endptr2, base);
if (endptr)
*endptr = endptr2;
if ((result == 0 || result == std::numeric_limits::min()
|| result == std::numeric_limits::max())
&& (errno || nptr == endptr2)) {
*ok = false;
return 0;
}
return result;
}
//qt_strtoll
/*
* Convert a string to a long long integer.
*
* Assumes that the upper and lower case
* alphabets and digits are each contiguous.
*/
long long
qt_strtoll(const char * nptr, char **endptr, int base)
{
const char *s;
unsigned long long acc;
char c;
unsigned long long cutoff;
int neg, any, cutlim;
/*
* Skip white space and pick up leading +/- sign if any.
* If base is 0, allow 0x for hex and 0 for octal, else
* assume decimal; if base is already 16, allow 0x.
*/
s = nptr;
do {
c = *s++;
} while (ascii_isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
} else {
neg = 0;
if (c == '+')
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X') &&
((s[1] >= '0' && s[1] <= '9') ||
(s[1] >= 'A' && s[1] <= 'F') ||
(s[1] >= 'a' && s[1] <= 'f'))) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;
if (base < 2 || base > 36)
goto noconv;
/*
* Compute the cutoff value between legal numbers and illegal
* numbers. That is the largest legal value, divided by the
* base. An input number that is greater than this value, if
* followed by a legal input character, is too big. One that
* is equal to this value may be valid or not; the limit
* between valid and invalid numbers is then based on the last
* digit. For instance, if the range for quads is
* [-9223372036854775808..9223372036854775807] and the input base
* is 10, cutoff will be set to 922337203685477580 and cutlim to
* either 7 (neg==0) or 8 (neg==1), meaning that if we have
* accumulated a value > 922337203685477580, or equal but the
* next digit is > 7 (or 8), the number is too big, and we will
* return a range error.
*
* Set 'any' if any `digits' consumed; make it negative to indicate
* overflow.
*/
cutoff = neg ? (unsigned long long)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
: LLONG_MAX;
cutlim = cutoff % base;
cutoff /= base;
for ( ; ; c = *s++) {
if (c >= '0' && c <= '9')
c -= '0';
else if (c >= 'A' && c <= 'Z')
c -= 'A' - 10;
else if (c >= 'a' && c <= 'z')
c -= 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = neg ? LLONG_MIN : LLONG_MAX;
errno = ERANGE;
} else if (!any) {
noconv:
errno = EINVAL;
} else if (neg)
acc = (unsigned long long) -(long long)acc;
if (endptr != NULL)
*endptr = const_cast(any ? s - 1 : nptr);
return (acc);
}
从后往前分析
// 这是一个将字符串转换为长整数的函数,它接受三个参数:
// 1. 一个指向要转换的字符串的指针 nptr。
// 2. 一个指向字符的指针 endptr,如果转换成功,endptr 将被设置为字符串中第一个无法被转换的字符的位置。
// 3. 一个整数 base,用于指定数字的基数,例如十进制、十六进制等。
long long qt_strtoll(const char * nptr, char **endptr, int base) {
const char *s; // s 用于保存字符串的当前位置
unsigned long long acc; // acc 用于保存累积的转换结果
char c; // c 用于保存当前字符
unsigned long long cutoff; // cutoff 用于保存转换的上限(基于正负和基数)
int neg, any, cutlim; // neg 用于标记数字是否为负数,any 用于标记是否有字符被成功转换,cutlim 用于保存累积的上限(基于基数)
// 跳过前导空格并获取可能存在的正负号
// 如果 base 为 0,允许使用 '0x' 表示十六进制,'0' 表示八进制,否则默认为十进制;如果 base 已经为 16,允许使用 '0x'。
// (以上注释内容是针对原文代码的逐行注释,所以这里不再翻译成英文)
// 此处通过一个 do-while 循环来跳过前导空格
// c 是当前的字符,通过递增 s 来获取下一个字符
s = nptr; // 设置 s 为字符串的起始位置
do {
c = *s++; // 获取下一个字符并递增 s
} while (ascii_isspace(c)); // 当 c 是空格字符时继续循环
// 如果 c 是 '-',设置 neg 为 1,并将 c 设置为下一个字符
// 如果 c 是 '+',设置 neg 为 0,并将 c 设置为下一个字符
if (c == '-') {
neg = 1; // 设置 neg 为 1
c = *s++; // 设置 c 为下一个字符并递增 s
} else {
neg = 0; // 设置 neg 为 0
if (c == '+') // 如果 c 是 '+',则设置 c 为下一个字符并递增 s
c = *s++; // 设置 c 为下一个字符并递增 s
}
// 如果 c 是 '0',并且接下来的字符是 'x' 或 'X',并且接下来的字符是十六进制数字(0-9, A-F 或 a-f),则将 base 设置为 16,并设置 c 为接下来的字符并递增 s。
if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X') && ((s[1] >= '0' && s[1] <= '9') || (s[1] >= 'A' && s[1] <= 'F') || (s[1] >= 'a' && s[1] <= 'f'))) {
c = s[1]; // 设置 c 为接下来的字符并递增 s
s += 2; // 将 s 向前移动两个位置(跳过 '0x' 或 '0X')
base = 16; // 设置 base 为 16
}
// 如果 base 是 0,则根据 c 的值将 base 设置回十进制。如果 c 是 '0',则将 base 设置回八进制;否则将 base 设置回十进制。
if (base == 0) { // 如果 base 是 0,则进行以下判断来设置 base 的值
base = (c == '0') ? 8 : 10; // 如果 c 是 '0',则将 base 设置回八进制;否则将 base 设置回十进制。同时设置 c 为接下来的字符并递增 s。
}
acc = 0; // 设置累积结果为0
any = 0; // 设置任何字符被成功转换的标志为0
cutoff = neg ? (unsigned long long)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX : LLONG_MAX; // 设置转换的上下限为正负的最大值
cutlim = cutoff % base; // 计算转换的上下限除以基数的结果
cutoff /= base; // 将转换的上下限除以基数得到新的上限
// 根据标志判断是否发生了溢出,如果溢出,则将累积结果设置为相应的最小值或最大值,并设置errno为ERANGE
if (any < 0) {
acc = neg ? LLONG_MIN : LLONG_MAX; // 设置累积结果为正负的最大值
errno = ERANGE; // 设置errno为ERANGE,表示溢出
} else if (!any) { // 如果没有任何字符被成功转换,则设置errno为EINVAL
errno = EINVAL; // 设置errno为EINVAL,表示无效的输入
}
long long qstrtoll(const char * nptr, const char **endptr, int base, bool *ok)
// 定义一个名为qstrtoll的函数,该函数将一个字符串转换为长整数。参数包括:要转换的字符串nptr,结束字符的指针endptr,转换的进制base,和一个指向布尔值的指针ok。
{
*ok = true; // 初始化ok为true,表示转换过程开始时没有错误。
errno = 0; // 初始化errno为0,用于记录可能发生的错误。
char *endptr2 = 0; // 定义一个新的字符指针endptr2并将其初始化为0。它用于存储转换结束的位置。
long long result = qt_strtoll(nptr, &endptr2, base); // 调用qt_strtoll函数进行实际的转换操作。将转换结果存储在result变量中,并将结束位置的指针存储在endptr2中。
if (endptr) // 检查endptr是否为非空指针。如果是,则执行下面的代码块。
*endptr = endptr2; // 将endptr指向的位置设置为转换结束的位置。
if ((result == 0 || result == std::numeric_limits::min() // 检查result是否为0或者等于long long的最小值。如果是,则执行下面的代码块。
|| result == std::numeric_limits::max()) // 或者等于long long的最大值。如果是,则执行下面的代码块。
&& (errno || nptr == endptr2)) { // 检查errno是否为非零值或者nptr是否等于endptr2。如果是,则执行下面的代码块。
*ok = false; // 将ok设置为false,表示发生了错误。
return 0; // 返回0,表示转换失败。
}
return result; // 返回转换结果result。
}
// qlonglong QLocaleData::bytearrayToLongLong(const char *num, int base, bool *ok)
// 定义一个名为bytearrayToLongLong的成员函数,该函数属于QLocaleData类,用于将字符数组转换为长整型数。
// 输入参数包括:一个指向字符数组的指针num,表示要转换的字符串;一个int型的base,表示转换的进制;一个指向布尔值的指针ok,用于返回转换是否成功的信息。
{
// 定义一个局部布尔变量_ok,用于存储转换是否成功的信息。
bool _ok;
// 定义一个指向字符的指针endptr,用于存储转换结束的位置。
const char *endptr;
// 检查输入的字符串是否为空,如果为空则返回错误。
if (*num == '\0') {
// 如果ok不为空,则将其设置为false并返回0。
if (ok != nullptr)
*ok = false;
// 返回0,表示转换失败。
return 0;
}
// 调用qstrtoll函数将字符串转换为长整型数,并将转换结果存储在l中,转换结束的位置存储在endptr中,转换的结果存储在_ok中。
qlonglong l = qstrtoll(num, &endptr, base, &_ok);
// 如果转换失败,则返回错误。
if (!_ok) {
// 如果ok不为空,则将其设置为false并返回0。
if (ok != nullptr)
*ok = false;
// 返回0,表示转换失败。
return 0;
}
// 检查转换结束的位置是否为字符串的末尾,如果不是,则跳过之后的空白字符。
if (*endptr != '\0') {
while (ascii_isspace(*endptr))
++endptr;
}
// 如果转换结束的位置不是字符串的末尾,说明在转换完一些数字之后遇到了非数字字符。
if (*endptr != '\0') {
// 在转换了一些数字之后遇到了一个非数字字符,设置ok为false并返回错误。
// we stopped at a non-digit character after converting some digits
if (ok != nullptr)
*ok = false;
// 返回0,表示转换失败。
return 0;
}
// 设置ok为true,表示转换成功。
if (ok != nullptr)
*ok = true;
// 返回转换结果。
return l;
}
// T toIntegral_helper(const QChar *data, int len, bool *ok, int base) 函数接受四个参数:
// 1. const QChar *data: 一个指向字符数据的指针,这些数据应该是要转换的数字字符串。
// 2. int len: 字符数据的长度。
// 3. bool *ok: 一个指向布尔值的指针,用于指示转换是否成功。如果转换成功,它将设置为true,否则设置为false。
// 4. int base: 转换的基数,例如十进制、十六进制等。
{
// ### Qt6: use std::conditional::value, qulonglong, qlonglong>::type
// 在Qt6中,建议使用std::conditional来选择正确的类型。如果T是无符号整数类型,选择qulonglong,否则选择qlonglong。
const bool isUnsigned = T(0) < T(-1); // 判断T是否是无符号整数类型,如果是,isUnsigned为true,否则为false。
// 使用std::conditional来选择正确的类型。如果是无符号整数,选择qulonglong,否则选择qlonglong。
typedef typename QtPrivate::QConditional::Type Int64;
// 使用std::conditional来选择正确的类型。如果是无符号整数,选择uint,否则选择int。
typedef typename QtPrivate::QConditional::Type Int32;
// 通过将len强制转换为Int32来选择正确的重载函数。这允许我们处理int和uint两种情况。
// we select the right overload by casting size() to int or uint
// toIntegral_helper为另一个重载函数
Int64 val = toIntegral_helper(data, Int32(len), ok, base);
// 检查转换后的值是否溢出。如果溢出,我们将ok设置为false,并将val设置为0。
if (T(val) != val) {
if (ok)
*ok = false;
val = 0;
}
return T(val); // 返回转换后的值。
}
QString QString::number(int n, int base)
{
return number(qlonglong(n), base);
}
//number()
QString QString::number(qlonglong n, int base)
{
#if defined(QT_CHECK_RANGE)
if (base < 2 || base > 36) {
qWarning("QString::setNum: Invalid base (%d)", base);
base = 10;
}
#endif
return QLocaleData::c()->longLongToString(n, -1, base);
}
//longLongToString
QString QLocaleData::longLongToString(qlonglong l, int precision,
int base, int width,
unsigned flags) const
{
return longLongToString(m_zero, m_group, m_plus, m_minus,
l, precision, base, width, flags);
}
QString QLocaleData::longLongToString(const QChar zero, const QChar group,
const QChar plus, const QChar minus,
qlonglong l, int precision,
int base, int width,
unsigned flags)
{
bool precision_not_specified = false;
if (precision == -1) {
precision_not_specified = true;
precision = 1;
}
bool negative = l < 0;
if (base != 10) {
// these are not supported by sprintf for octal and hex
flags &= ~AlwaysShowSign;
flags &= ~BlankBeforePositive;
negative = false; // neither are negative numbers
}
QT_WARNING_PUSH
/* "unary minus operator applied to unsigned type, result still unsigned" */
QT_WARNING_DISABLE_MSVC(4146)
/*
Negating std::numeric_limits::min() hits undefined behavior, so
taking an absolute value has to cast to unsigned to change sign.
*/
QString num_str = qulltoa(negative ? -qulonglong(l) : qulonglong(l), base, zero);
QT_WARNING_POP
uint cnt_thousand_sep = 0;
if (flags & ThousandsGroup && base == 10) {
for (int i = num_str.length() - 3; i > 0; i -= 3) {
num_str.insert(i, group);
++cnt_thousand_sep;
}
}
for (int i = num_str.length()/* - cnt_thousand_sep*/; i < precision; ++i)
num_str.prepend(base == 10 ? zero : QChar::fromLatin1('0'));
if ((flags & ShowBase)
&& base == 8
&& (num_str.isEmpty() || num_str[0].unicode() != QLatin1Char('0')))
num_str.prepend(QLatin1Char('0'));
// LeftAdjusted overrides this flag ZeroPadded. sprintf only padds
// when precision is not specified in the format string
bool zero_padded = flags & ZeroPadded
&& !(flags & LeftAdjusted)
&& precision_not_specified;
if (zero_padded) {
int num_pad_chars = width - num_str.length();
// leave space for the sign
if (negative
|| flags & AlwaysShowSign
|| flags & BlankBeforePositive)
--num_pad_chars;
// leave space for optional '0x' in hex form
if (base == 16 && (flags & ShowBase))
num_pad_chars -= 2;
// leave space for optional '0b' in binary form
else if (base == 2 && (flags & ShowBase))
num_pad_chars -= 2;
for (int i = 0; i < num_pad_chars; ++i)
num_str.prepend(base == 10 ? zero : QChar::fromLatin1('0'));
}
if (flags & CapitalEorX)
num_str = std::move(num_str).toUpper();
if (base == 16 && (flags & ShowBase))
num_str.prepend(QLatin1String(flags & UppercaseBase ? "0X" : "0x"));
if (base == 2 && (flags & ShowBase))
num_str.prepend(QLatin1String(flags & UppercaseBase ? "0B" : "0b"));
// add sign
if (negative)
num_str.prepend(minus);
else if (flags & AlwaysShowSign)
num_str.prepend(plus);
else if (flags & BlankBeforePositive)
num_str.prepend(QLatin1Char(' '));
return num_str;
}
QString QLocaleData::longLongToString(const QChar zero, const QChar group,const QChar plus,const QChar minus, qlonglong l, int precision,int base, int width, unsigned flags)
{
// 判断精度是否未指定
bool precision_not_specified = false;
if (precision == -1) {
precision_not_specified = true;
precision = 1;
}
// 判断数值是否为负数
bool negative = l < 0;
if (base != 10) {
// 当进制不为10时,以下标志将被禁用:始终显示符号,以及在正数前空格对齐
// these are not supported by sprintf for octal and hex
flags &= ~AlwaysShowSign;
flags &= ~BlankBeforePositive;
negative = false; // 负数也不支持
}
// 此处使用了QT的警告控制宏,禁用MSVC的4146警告,该警告通常表示有未使用的变量
QT_WARNING_PUSH
QT_WARNING_DISABLE_MSVC(4146)
// 使用qulltoa函数将长整型数值转换为字符串,参数包括是否为负数、进制、零字符
QString num_str = qulltoa(negative ? -qulonglong(l) : qulonglong(l), base, zero);
QT_WARNING_POP
uint cnt_thousand_sep = 0;
// 如果设置了千位分隔标志并且进制为10,则对字符串进行千位分隔
if (flags & ThousandsGroup && base == 10) {
for (int i = num_str.length() - 3; i > 0; i -= 3) {
num_str.insert(i, group);
++cnt_thousand_sep;
}
}
// 如果精度不够,则在前面补零
for (int i = num_str.length()/* - cnt_thousand_sep*/; i < precision; ++i)
num_str.prepend(base == 10 ? zero : QChar::fromLatin1('0'));
// 如果设置了显示基数的标志,并且进制为8,并且字符串为空或者第一个字符不是'0',则在前面添加'0'
if ((flags & ShowBase)
&& base == 8
&& (num_str.isEmpty() || num_str[0].unicode() != QLatin1Char('0')))
num_str.prepend(QLatin1Char('0'));
// 判断是否需要零填充,并且没有左对齐标志,并且精度未指定
bool zero_padded = flags & ZeroPadded
&& !(flags & LeftAdjusted)
&& precision_not_specified;
//如果zero_padded为true,则计算需要在数字字符串前填充的零的数量。同时,如果需要,还会为负号、正号留出空间,以及在十六进制或二进制显示时,为基数标志留出空间。之后,它循环填充零,然后返回格式化的字符串。
if (zero_padded) {
int num_pad_chars = width - num_str.length();
// leave space for the sign
if (negative
|| flags & AlwaysShowSign
|| flags & BlankBeforePositive)
--num_pad_chars;
// leave space for optional '0x' in hex form
if (base == 16 && (flags & ShowBase))
num_pad_chars -= 2;
// leave space for optional '0b' in binary form
else if (base == 2 && (flags & ShowBase))
num_pad_chars -= 2;
for (int i = 0; i < num_pad_chars; ++i)
num_str.prepend(base == 10 ? zero : QChar::fromLatin1('0'));
}
//如果设置了CapitalEorX标志,会将数字字符串转换为大写。
if (flags & CapitalEorX)
num_str = std::move(num_str).toUpper();
//如果进制为16且显示基数的标志被设置,会在数字字符串前添加0x或0X。
//如果进制为2且显示基数的标志被设置,会在数字字符串前添加0b或0B。
if (base == 16 && (flags & ShowBase))
num_str.prepend(QLatin1String(flags & UppercaseBase ? "0X" : "0x"));
if (base == 2 && (flags & ShowBase))
num_str.prepend(QLatin1String(flags & UppercaseBase ? "0B" : "0b"));
//最后,如果数字是负数,会在字符串前添加负号;如果设置了始终显示符号的标志,会在字符串前添加正号;如果设置了空格在正数前的标志,会在字符串前添加空格。
// add sign
if (negative)
num_str.prepend(minus);
else if (flags & AlwaysShowSign)
num_str.prepend(plus);
else if (flags & BlankBeforePositive)
num_str.prepend(QLatin1Char(' '));
return num_str;
}
//这段代码的主要功能是将一个qulonglong类型的整数转化为指定基数的字符串形式。其中处理了两种特殊情况:当基数不为10或者_zero字符为'0'时,会使用数字0到9来表示余数;否则会使用_zero字符加上余数的值来表示余数。
// 定义一个函数,将qulonglong类型的数转化为字符串,基数在10-36之间,使用QChar类型的_zero字符来表示零
QString qulltoa(qulonglong l, int base, const QChar _zero)
// 定义一个长度为65的ushort数组,用于存储整数l被基数除后的余数,最多可以表示65位的数(对于uint64_t类型来说)
ushort buff[65]; // length of MAX_ULLONG in base 2
// 定义一个指向buff数组的指针p,初始化为数组末尾的地址
ushort *p = buff + 65;
// 如果基数不为10,或者_zero字符的unicode值为'0',则执行下面的循环
if (base != 10 || _zero.unicode() == '0') {
// 循环直到整数l为0
while (l != 0) {
// 计算l除以基数的余数
int c = l % base;
// 将指针p减1,因为我们将向数组的头部填充数据
--p;
// 如果余数小于10,则将其转化为字符'0'到'9',并存储在指针p指向的地址
if (c < 10)
*p = '0' + c;
// 如果余数大于等于10,则将其转化为字符'a'到'z',并存储在指针p指向的地址
else
*p = c - 10 + 'a';
// 计算l除以基数的商,更新l的值
l /= base;
}
}
// 如果基数为10,且_zero字符的unicode值不为'0',则执行下面的循环
else {
// 循环直到整数l为0
while (l != 0) {
// 计算l除以基数的余数
int c = l % base;
// 将余数加上_zero的unicode值,得到对应的字符,并存储在指针p指向的地址,然后将指针p减1
*(--p) = _zero.unicode() + c;
// 计算l除以基数的商,更新l的值
l /= base;
}
}
// 将数组buff中的数据(存储在p指向的地址到buff数组末尾)转化为QString类型并返回,数组的长度为65减去指针p和数组buff的差值(即p-buff)
return QString(reinterpret_cast(p), 65 - (p - buff));