在不访问库的情况下将整数转换为字符串
Posted
技术标签:
【中文标题】在不访问库的情况下将整数转换为字符串【英文标题】:Convert integer to string without access to libraries 【发布时间】:2011-04-28 07:04:00 【问题描述】:我最近阅读了一个样本求职面试问题:
写一个函数来转换一个整数 到一个字符串。假设你没有 访问库函数,即 itoa() 等...
你会怎么做?
【问题讨论】:
家庭作业?你会怎么写一个以 7 为底的整数?计算机也必须这样做(以 10 为基数)atoi()
更有趣,因为您必须处理前导空格、一元正负以及正负溢出(除其他外)。
【参考方案1】:
快速尝试:(编辑为处理负数)
int n = INT_MIN;
char buffer[50];
int i = 0;
bool isNeg = n<0;
unsigned int n1 = isNeg ? -n : n;
while(n1!=0)
buffer[i++] = n1%10+'0';
n1=n1/10;
if(isNeg)
buffer[i++] = '-';
buffer[i] = '\0';
for(int t = 0; t < i/2; t++)
buffer[t] ^= buffer[i-t-1];
buffer[i-t-1] ^= buffer[t];
buffer[t] ^= buffer[i-t-1];
if(n == 0)
buffer[0] = '0';
buffer[1] = '\0';
printf(buffer);
【讨论】:
关于建议同样的事情。放入字符串和反向字符串。 这无法在二进制补码机器上正确处理INT_MIN
。
base为16的情况如何处理?
@Mike 最好的方法是将其转换为十进制并做同样的事情。
当 n 为 0(零)时,此实现也会失败。【参考方案2】:
在网上查看 itoa 实现将为您提供很好的示例。这是一个,避免在末尾反转字符串。它依赖于静态缓冲区,因此如果您将其重用于不同的值,请小心。
char* itoa(int val, int base)
static char buf[32] = 0;
int i = 30;
for(; val && i ; --i, val /= base)
buf[i] = "0123456789abcdef"[val % base];
return &buf[i+1];
【讨论】:
当 val 为 0(零)时,此实现将失败。 这很聪明。我喜欢它。 它也不处理负数,而是短的道具。【参考方案3】:该算法在英文中很容易看到。
给定一个整数,例如123
除以 10 => 123/10。产量,结果 = 12,余数 = 3
将 30h 添加到 3 并压入堆栈(添加 30h 会将 3 转换为 ASCII 表示)
重复步骤 1 直到结果
将 30h 添加到结果并存储在堆栈中
堆栈包含按 | 顺序排列的数字1 | 2 | 3 | ...
【讨论】:
最好添加“0”,而不是在普通编码中恰好正确的神奇十六进制常量。不需要时无需将代码绑定到 ASCII。 添加'0'
is 将代码绑定到 ASCII - 你假设'0'..'9' 是按顺序排列的。无论编译器的代码集如何,添加 0x30 都会产生 ASCII 输出。如果编译器的代码集是 ASCII,则添加“0”会产生相同的输出,否则可能会产生乱码。话虽如此,如果我的编译器不使用 ASCII 值作为字符常量,我会替换它!
@NicholasWilson C 标准要求数字数字按顺序排列;源和执行字符集。【参考方案4】:
我要记住,所有数字字符在 ASCII 字符集中都是按升序排列的,它们之间没有其他字符。
我还会反复使用/
和%
运算符。
我将如何获取字符串的内存将取决于您未提供的信息。
【讨论】:
为什么投反对票?数字值之间的关系不是特定于 ASCII 的。这是 C 标准所要求的。这个答案不太好,但我认为这是故意含糊的,因为问题是家庭作业。 +1 以弥补可笑的反对票。 我怀疑投反对票,因为我没有编写任何代码。我没有编写任何代码,因为它看起来像是 家庭作业 问题,或者任何作为 C 程序员面试工作的人都应该能够回答的问题类型. @R. “撤销”其他人的投票权不是你的工作。 我不认为基于当前分数太高或太低的感觉投票是不合理的;天真地,我希望很多人都这样做。当然,这似乎是一个更适合元的讨论,所以如果你认为值得进一步讨论,也许在那里提出一个问题..? @LightnessRacesinOrbit 在掩埋垃圾的同时有效地使用自己的投票来宣传有用的信息是每个用户的工作。【参考方案5】:假设它是十进制的,那么像这样:
int num = ...;
char res[MaxDigitCount];
int len = 0;
for(; num > 0; ++len)
res[len] = num%10+'0';
num/=10;
res[len] = 0; //null-terminating
//now we need to reverse res
for(int i = 0; i < len/2; ++i)
char c = res[i]; res[i] = res[len-i-1]; res[len-i-1] = c;
【讨论】:
这不处理负数,MaxDigitCount
需要为 MaxDigitCountPlusTwo
以说明一元减号和空终止符。
当 num 为 0(零)时,此实现也会失败。【参考方案6】:
itoa()
函数的实现似乎是一项简单的任务,但实际上您必须处理与您的确切需求相关的许多方面。我想在面试中你应该提供一些关于你的解决方案的细节,而不是复制可以在谷歌中找到的解决方案 (http://en.wikipedia.org/wiki/Itoa)
以下是您可能想问自己或面试官的一些问题:
字符串应该放在哪里(malloced?由用户传递?静态变量?) 我应该支持签名号码吗? 我应该支持浮点吗? 我应该支持其他基地而不是 10 个基地吗? 我们需要任何输入检查吗? 输出字符串的长度是否有限制?等等。
【讨论】:
为什么 itoa 会处理浮点数? itoa 是整数到 ASCII 我只是想说明一点。在面试中,你提出的问题和解决问题的方法就像代码一样重要。代码当然应该是完美的。【参考方案7】:在不访问库的情况下将整数转换为字符串
先将最低有效数字转换为字符,然后再转换为更高有效数字。
通常我会将生成的 string 移动到位,但递归允许使用一些紧凑的代码跳过该步骤。
在myitoa_helper()
中使用neg_a
可以避免INT_MIN
的未定义行为。
// Return character one past end of character digits.
static char *myitoa_helper(char *dest, int neg_a)
if (neg_a <= -10)
dest = myitoa_helper(dest, neg_a / 10);
*dest = (char) ('0' - neg_a % 10);
return dest + 1;
char *myitoa(char *dest, int a)
if (a >= 0)
*myitoa_helper(dest, -a) = '\0';
else
*dest = '-';
*myitoa_helper(dest + 1, a) = '\0';
return dest;
void myitoa_test(int a)
char s[100];
memset(s, 'x', sizeof s);
printf("%11d <%s>\n", a, myitoa(s, a));
测试代码和输出
#include "limits.h"
#include "stdio.h"
int main(void)
const int a[] = INT_MIN, INT_MIN + 1, -42, -1, 0, 1, 2, 9, 10, 99, 100,
INT_MAX - 1, INT_MAX;
for (unsigned i = 0; i < sizeof a / sizeof a[0]; i++)
myitoa_test(a[i]);
return 0;
-2147483648 <-2147483648>
-2147483647 <-2147483647>
-42 <-42>
-1 <-1>
0 <0>
1 <1>
2 <2>
9 <9>
10 <10>
99 <99>
100 <100>
2147483646 <2147483646>
2147483647 <2147483647>
【讨论】:
【参考方案8】:越快越好?
unsigned countDigits(long long x)
int i = 1;
while ((x /= 10) && ++i);
return i;
unsigned getNumDigits(long long x)
x < 0 ? x = -x : 0;
return
x < 10 ? 1 :
x < 100 ? 2 :
x < 1000 ? 3 :
x < 10000 ? 4 :
x < 100000 ? 5 :
x < 1000000 ? 6 :
x < 10000000 ? 7 :
x < 100000000 ? 8 :
x < 1000000000 ? 9 :
x < 10000000000 ? 10 : countDigits(x);
#define tochar(x) '0' + x
void tostr(char* dest, long long x)
unsigned i = getNumDigits(x);
char negative = x < 0;
if (negative && (*dest = '-') & (x = -x) & i++);
*(dest + i) = 0;
while ((i > negative) && (*(dest + (--i)) = tochar(((x) % 10))) | (x /= 10));
如果要调试,可以将条件(指令)拆分成
while 范围内的代码行 .
【讨论】:
我真的很喜欢你的回答,但我认为可以通过一些调整来改进它。例如char tochar(unsigned short from)
可以优化:inline char tochar(unsigned char from) if (from > 9) return '0'; return '0' + from;
在这个特定的例子中可以删除边缘情况,因为你确定你总是会传递给 tochar
数字从 0 到 9。
@Cristian 这是我的一个老答案,所以相信当我说我现在看到我的答案时,我的想法完全一样!哈哈..现在我已经编辑了它,包括其他一些东西,因为它提到了“没有库”,看起来我在 2 年前在这里盲目使用了calloc
,显然我记得需要一个库~所以,谢谢。现在更好了。 :)【参考方案9】:
我遇到了这个问题,所以我决定放弃我通常使用的代码:
char *SignedIntToStr(char *Dest, signed int Number, register unsigned char Base)
if (Base < 2 || Base > 36)
return (char *)0;
register unsigned char Digits = 1;
register unsigned int CurrentPlaceValue = 1;
for (register unsigned int T = Number/Base; T; T /= Base)
CurrentPlaceValue *= Base;
Digits++;
if (!Dest)
Dest = malloc(Digits+(Number < 0)+1);
char *const RDest = Dest;
if (Number < 0)
Number = -Number;
*Dest = '-';
Dest++;
for (register unsigned char i = 0; i < Digits; i++)
register unsigned char Digit = (Number/CurrentPlaceValue);
Dest[i] = (Digit < 10? '0' : 87)+Digit;
Number %= CurrentPlaceValue;
CurrentPlaceValue /= Base;
Dest[Digits] = '\0';
return RDest;
#include <stdio.h>
int main(int argc, char *argv[])
char String[32];
puts(SignedIntToStr(String, -100, 16));
return 0;
如果将 NULL 传递给 Dest,这将自动分配内存。否则它将写入 Dest。
【讨论】:
【参考方案10】:这是一个简单的方法,但我怀疑如果你在不理解和解释的情况下按原样提交,你的老师会知道你只是从网上复制的:
char *pru(unsigned x, char *eob)
do *--eob = x%10; while (x/=10);
return eob;
char *pri(int x, char *eob)
eob = fmtu(x<0?-x:x, eob);
if (x<0) *--eob='-';
return eob;
可以进行各种改进,特别是如果您想有效地支持大于字的整数大小,最高可达intmax_t
。我会让你弄清楚这些函数的调用方式。
【讨论】:
我想你的意思是pru
,你写了fmtu
,对吧?而且,这些名字很恶心。它们是否应该被理解为“打印无符号”、“打印整数”和“无符号格式”?此外,这假设一个固定长度的字符串或内存缓冲区,因为它不处理 NUL 字符,而且缓冲区足够长,可以向后遍历。您应该在代码中将所有这些限制记录为注释或更多语义标识符。
*--eob = x%10;
是错误的。 -x
潜在的 UB,对于 string 没有 null 字符。【参考方案11】:
略长于解:
static char*
itoa(int n, char s[])
int i, sign;
if ((sign = n) < 0)
n = -n;
i = 0;
do
s[i++] = n % 10 + '0';
while ((n /= 10) > 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
return s;
反向:
int strlen(const char* str)
int i = 0;
while (str != '\0')
i++;
str++;
return i;
static void
reverse(char s[])
int i, j;
char c;
for (i = 0, j = strlen(s)-1; i<j; i++, j--)
c = s[i];
s[i] = s[j];
s[j] = c;
虽然这里的决定 davolno long 是一些对初学者有用的功能。希望对您有所帮助。
【讨论】:
【参考方案12】:这是我能想到的最短的函数:
正确处理所有带符号的 32 位整数,包括 0、MIN_INT32、MAX_INT32。
返回一个可以立即打印的值,例如:printf("%s\n", GetDigits(-123))
请评论改进:
static const char LARGEST_NEGATIVE[] = "-2147483648";
static char* GetDigits(int32_t x)
char* buffer = (char*) calloc(sizeof(LARGEST_NEGATIVE), 1);
int negative = x < 0;
if (negative)
if (x + (1 << 31) == 0) // if x is the largest negative number
memcpy(buffer, LARGEST_NEGATIVE, sizeof(LARGEST_NEGATIVE));
return buffer;
x *= -1;
// storing digits in reversed order
int length = 0;
do
buffer[length++] = x % 10 + '0';
x /= 10;
while (x > 0);
if (negative)
buffer[length++] = '-'; // appending minus
// reversing digits
for (int i = 0; i < length / 2; i++)
char temp = buffer[i];
buffer[i] = buffer[length-1 - i];
buffer[length-1 - i] = temp;
return buffer;
【讨论】:
以上是关于在不访问库的情况下将整数转换为字符串的主要内容,如果未能解决你的问题,请参考以下文章
如何在不使用 ToString() 的情况下将 Int 转换为 C# 中的字符串?
如何在不使用 DateTimeFormatter 的情况下将字符串转换为 LocalDate [重复]
如何在不将 LocalDateTime 字段转换为扩展的 json 对象的情况下将 java 对象转换为简单的 json 字符串?