C言語で_1123456789
_から_1,123,456,789
_の数値をフォーマットするにはどうすればよいですか? printf("%'10d\n", 1123456789);
を使用してみましたが、機能しません。
何かアドバイスはありますか?ソリューションが単純であるほど良い。
Printfが_'
_フラグをサポートしている場合(POSIX 2008 printf()
で必要な場合)、ロケールを適切に設定するだけで可能です。例:
_#include <stdio.h>
#include <locale.h>
int main(void)
{
setlocale(LC_NUMERIC, "");
printf("%'d\n", 1123456789);
return 0;
}
_
そしてビルドと実行:
_$ ./example
1,123,456,789
_
Mac OS XおよびLinux(Ubuntu 10.10)でテスト済み。
次のように再帰的に実行できます(2の補数を使用している場合は、INT_MIN
に注意してください。それを管理するには追加のコードが必要です)。
void printfcomma2 (int n) {
if (n < 1000) {
printf ("%d", n);
return;
}
printfcomma2 (n/1000);
printf (",%03d", n%1000);
}
void printfcomma (int n) {
if (n < 0) {
printf ("-");
n = -n;
}
printfcomma2 (n);
}
要約:
printfcomma
を整数で呼び出します。負の数の特殊なケースは、単に「-」を出力し、数を正にすることで処理されます(これはINT_MIN
で機能しないビットです)。printfcomma2
を入力すると、1,000未満の数字が印刷されて返されます。everyレベルで負の数をチェックする際に不必要な処理を行いますが、より簡潔なバージョンもあります(再帰レベルの数に制限があるため、これは重要ではありません)。これはテスト用の完全なプログラムです。
#include <stdio.h>
void printfcomma (int n) {
if (n < 0) {
printf ("-");
printfcomma (-n);
return;
}
if (n < 1000) {
printf ("%d", n);
return;
}
printfcomma (n/1000);
printf (",%03d", n%1000);
}
int main (void) {
int x[] = {-1234567890, -123456, -12345, -1000, -999, -1,
0, 1, 999, 1000, 12345, 123456, 1234567890};
int *px = x;
while (px != &(x[sizeof(x)/sizeof(*x)])) {
printf ("%-15d: ", *px);
printfcomma (*px);
printf ("\n");
px++;
}
return 0;
}
出力は次のとおりです。
-1234567890 : -1,234,567,890
-123456 : -123,456
-12345 : -12,345
-1000 : -1,000
-999 : -999
-1 : -1
0 : 0
1 : 1
999 : 999
1000 : 1,000
12345 : 12,345
123456 : 123,456
1234567890 : 1,234,567,890
再帰を信頼しない人のための反復的な解決策(ただし、再帰の唯一の問題はスタックスペースである傾向がありますが、64ビット整数であっても数レベルの深さであるため、ここでは問題になりません):
void printfcomma (int n) {
int n2 = 0;
int scale = 1;
if (n < 0) {
printf ("-");
n = -n;
}
while (n >= 1000) {
n2 = n2 + scale * (n % 1000);
n /= 1000;
scale *= 1000;
}
printf ("%d", n);
while (scale != 1) {
scale /= 1000;
n = n2 / scale;
n2 = n2 % scale;
printf (",%03d", n);
}
}
どちらも2,147,483,647
に対してINT_MAX
を生成します。
これは非常に単純な実装です。この関数にはnoエラーチェックが含まれます。バッファーサイズは呼び出し元によって検証される必要があります。また、負の数では機能しません。このような改善は、読者の課題として残されています。
void format_commas(int n, char *out)
{
int c;
char buf[20];
char *p;
sprintf(buf, "%d", n);
c = 2 - strlen(buf) % 3;
for (p = buf; *p != 0; p++) {
*out++ = *p;
if (c == 1) {
*out++ = ',';
}
c = (c + 1) % 3;
}
*--out = 0;
}
エガッド!私はこれをLinuxでgcc/g ++とglibcを使用して常に実行しています。
#include <stdio.h>
#include <locale.h>
int main()
{
int bignum=12345678;
setlocale(LC_ALL,"");
printf("Big number: %'d\n",bignum);
return 0;
}
以下を出力します。
大きい数:12,345,678
そこにある「setlocale」呼び出しを覚えておく必要があります。そうしないと、何もフォーマットされません。
おそらく、ロケールに対応したバージョンが面白いでしょう。
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <limits.h>
static int next_group(char const **grouping) {
if ((*grouping)[1] == CHAR_MAX)
return 0;
if ((*grouping)[1] != '\0')
++*grouping;
return **grouping;
}
size_t commafmt(char *buf, /* Buffer for formatted string */
int bufsize, /* Size of buffer */
long N) /* Number to convert */
{
int i;
int len = 1;
int posn = 1;
int sign = 1;
char *ptr = buf + bufsize - 1;
struct lconv *fmt_info = localeconv();
char const *tsep = fmt_info->thousands_sep;
char const *group = fmt_info->grouping;
char const *neg = fmt_info->negative_sign;
size_t sep_len = strlen(tsep);
size_t group_len = strlen(group);
size_t neg_len = strlen(neg);
int places = (int)*group;
if (bufsize < 2)
{
ABORT:
*buf = '\0';
return 0;
}
*ptr-- = '\0';
--bufsize;
if (N < 0L)
{
sign = -1;
N = -N;
}
for ( ; len <= bufsize; ++len, ++posn)
{
*ptr-- = (char)((N % 10L) + '0');
if (0L == (N /= 10L))
break;
if (places && (0 == (posn % places)))
{
places = next_group(&group);
for (int i=sep_len; i>0; i--) {
*ptr-- = tsep[i-1];
if (++len >= bufsize)
goto ABORT;
}
}
if (len >= bufsize)
goto ABORT;
}
if (sign < 0)
{
if (len >= bufsize)
goto ABORT;
for (int i=neg_len; i>0; i--) {
*ptr-- = neg[i-1];
if (++len >= bufsize)
goto ABORT;
}
}
memmove(buf, ++ptr, len + 1);
return (size_t)len;
}
#ifdef TEST
#include <stdio.h>
#define elements(x) (sizeof(x)/sizeof(x[0]))
void show(long i) {
char buffer[32];
commafmt(buffer, sizeof(buffer), i);
printf("%s\n", buffer);
commafmt(buffer, sizeof(buffer), -i);
printf("%s\n", buffer);
}
int main() {
long inputs[] = {1, 12, 123, 1234, 12345, 123456, 1234567, 12345678 };
for (int i=0; i<elements(inputs); i++) {
setlocale(LC_ALL, "");
show(inputs[i]);
}
return 0;
}
#endif
これにはバグがあります(ただし、かなりマイナーだと思われます)。 2の補数ハードウェアでは、負の数をN = -N;
を使用して同等の正の数に変換しようとするため、最も負の数を正しく変換しません。2の補数では、最大の負の数には対応するより大きなタイプに昇格しない限り、正の数。これを回避する1つの方法は、対応する符号なし型の数を昇格させることです(ただし、それはやや重要です)。
再帰または文字列処理なしの数学的アプローチ:
#include <stdio.h>
#include <math.h>
void print_number( int n )
{
int order_of_magnitude = (n == 0) ? 1 : (int)pow( 10, ((int)floor(log10(abs(n))) / 3) * 3 ) ;
printf( "%d", n / order_of_magnitude ) ;
for( n = abs( n ) % order_of_magnitude, order_of_magnitude /= 1000;
order_of_magnitude > 0;
n %= order_of_magnitude, order_of_magnitude /= 1000 )
{
printf( ",%03d", abs(n / order_of_magnitude) ) ;
}
}
原則としてPaxの再帰的解法に似ていますが、事前に大きさのオーダーを計算することにより、再帰が回避されます(おそらくかなりの費用がかかります)。
また、数千を区切るために使用される実際の文字はロケール固有であることに注意してください。
編集:改善については、以下の@Chuxのコメントを参照してください。
@Greg Hewgillに基づきますが、負の数を考慮して文字列サイズを返します。
size_t str_format_int_grouped(char dst[16], int num)
{
char src[16];
char *p_src = src;
char *p_dst = dst;
const char separator = ',';
int num_len, commas;
num_len = sprintf(src, "%d", num);
if (*p_src == '-') {
*p_dst++ = *p_src++;
num_len--;
}
for (commas = 2 - num_len % 3;
*p_src;
commas = (commas + 1) % 3)
{
*p_dst++ = *p_src++;
if (commas == 1) {
*p_dst++ = separator;
}
}
*--p_dst = '\0';
return (size_t)(p_dst - dst);
}
別の解決策は、結果をint配列に保存することにより、long long intが範囲内の数を処理できるため、7の場合の最大サイズ9,223,372,036,854,775,807から-9,223,372,036,854,775,807 _note it is not an unsigned
_
非再帰印刷機能
_static void printNumber (int numbers[8], int loc, int negative)
{
if (negative)
{
printf("-");
}
if (numbers[1]==-1)//one number
{
printf("%d ", numbers[0]);
}
else
{
printf("%d,", numbers[loc]);
while(loc--)
{
if(loc==0)
{// last number
printf("%03d ", numbers[loc]);
break;
}
else
{ // number in between
printf("%03d,", numbers[loc]);
}
}
}
}
_
メイン関数呼び出し
_static void getNumWcommas (long long int n, int numbers[8])
{
int i;
int negative=0;
if (n < 0)
{
negative = 1;
n = -n;
}
for(i = 0; i<7; i++)
{
if (n < 1000)
{
numbers[i] = n;
numbers[i+1] = -1;
break;
}
numbers[i] = n%1000;
n/=1000;
}
printNumber(numbers, i, negative);// non recursive print
}
_
出力のテスト
_-9223372036854775807: -9,223,372,036,854,775,807
-1234567890 : -1,234,567,890
-123456 : -123,456
-12345 : -12,345
-1000 : -1,000
-999 : -999
-1 : -1
0 : 0
1 : 1
999 : 999
1000 : 1,000
12345 : 12,345
123456 : 123,456
1234567890 : 1,234,567,890
9223372036854775807 : 9,223,372,036,854,775,807
_
main()クラス内
_int numberSeperated[8];
long long int number = 1234567890LL;
getNumWcommas(number, numberSeperated );
_
印刷がすべて必要な場合は、_int numberSeperated[8];
_を関数getNumWcommas
内に移動し、このように呼び出しますgetNumWcommas(number);
この種の10進数字フォーマットの最もスリムで、サイズと速度の効率的な実装を次に示します。
const char *formatNumber (
int value,
char *endOfbuffer,
bool plus)
{
int savedValue;
int charCount;
savedValue = value;
if (unlikely (value < 0))
value = - value;
*--endOfbuffer = 0;
charCount = -1;
do
{
if (unlikely (++charCount == 3))
{
charCount = 0;
*--endOfbuffer = ',';
}
*--endOfbuffer = (char) (value % 10 + '0');
}
while ((value /= 10) != 0);
if (unlikely (savedValue < 0))
*--endOfbuffer = '-';
else if (unlikely (plus))
*--endOfbuffer = '+';
return endOfbuffer;
}
次のように使用します。
char buffer[16];
fprintf (stderr, "test : %s.", formatNumber (1234567890, buffer + 16, true));
出力:
test : +1,234,567,890.
いくつかの利点:
逆順の書式設定のために文字列バッファの終わりをとる関数。最後に、生成された文字列(strrev)を尊重する必要はありません。
この関数は、後のアルゴリズムで使用できる1つの文字列を生成します。複数のprintf/sprintf呼び出しに依存することも必要とすることもありません。これはひどく遅く、常にコンテキスト固有です。
私の答えは、質問の図のように結果を正確にフォーマットしませんが、実際のニーズを満たすかもしれません単純なワンライナーまたはマクロを使用する場合があります。必要に応じて、さらに千グループを生成するように拡張できます。
結果は次の例のようになります。
Value: 0'000'012'345
コード:
printf("Value: %llu'%03lu'%03lu'%03lu\n", (value / 1000 / 1000 / 1000), (value / 1000 / 1000) % 1000, (value / 1000) % 1000, value % 1000);
別の反復関数
int p(int n) {
if(n < 0) {
printf("-");
n = -n;
}
int a[sizeof(int) * CHAR_BIT / 3] = { 0 };
int *pa = a;
while(n > 0) {
*++pa = n % 1000;
n /= 1000;
}
printf("%d", *pa);
while(pa > a + 1) {
printf(",%03d", *--pa);
}
}
私はCプログラミングの初心者です。これが私の簡単なコードです。
int main()
{
// 1223 => 1,223
int n;
int a[10];
printf(" n: ");
scanf_s("%d", &n);
int i = 0;
while (n > 0)
{
int temp = n % 1000;
a[i] = temp;
n /= 1000;
i++;
}
for (int j = i - 1; j >= 0; j--)
{
if (j == 0)
{
printf("%d.", a[j]);
}
else printf("%d,",a[j]);
}
getch();
return 0;
}
安全なformat_commas、負の数値を使用:
VS <2015はsnprintfを実装していないため、これを行う必要があります
#if defined(_WIN32)
#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__)
#endif
その後
char* format_commas(int n, char *out)
{
int c;
char buf[100];
char *p;
char* q = out; // Backup pointer for return...
if (n < 0)
{
*out++ = '-';
n = abs(n);
}
snprintf(buf, 100, "%d", n);
c = 2 - strlen(buf) % 3;
for (p = buf; *p != 0; p++) {
*out++ = *p;
if (c == 1) {
*out++ = '\'';
}
c = (c + 1) % 3;
}
*--out = 0;
return q;
}
使用例:
size_t currentSize = getCurrentRSS();
size_t peakSize = getPeakRSS();
printf("Current size: %d\n", currentSize);
printf("Peak size: %d\n\n\n", peakSize);
char* szcurrentSize = (char*)malloc(100 * sizeof(char));
char* szpeakSize = (char*)malloc(100 * sizeof(char));
printf("Current size (f): %s\n", format_commas((int)currentSize, szcurrentSize));
printf("Peak size (f): %s\n", format_commas((int)currentSize, szpeakSize));
free(szcurrentSize);
free(szpeakSize);
Cでこれを行う簡単な方法はありません。int-to-string関数を変更するだけです。
void format_number(int n, char * out) {
int i;
int digit;
int out_index = 0;
for (i = n; i != 0; i /= 10) {
digit = i % 10;
if ((out_index + 1) % 4 == 0) {
out[out_index++] = ',';
}
out[out_index++] = digit + '0';
}
out[out_index] = '\0';
// then you reverse the out string as it was converted backwards (it's easier that way).
// I'll let you figure that one out.
strrev(out);
}
#include <stdio.h>
void punt(long long n){
char s[28];
int i = 27;
if(n<0){n=-n; putchar('-');}
do{
s[i--] = n%10 + '0';
if(!(i%4) && n>9)s[i--]='.';
n /= 10;
}while(n);
puts(&s[++i]);
}
int main(){
punt(2134567890);
punt(987);
punt(9876);
punt(-987);
punt(-9876);
punt(-654321);
punt(0);
punt(1000000000);
punt(0x7FFFFFFFFFFFFFFF);
punt(0x8000000000000001); // -max + 1 ...
}
私のソリューションはを使用しています。の代わりに、これを変更するのは読者に任されています。
@paxdiabloソリューションの修正バージョンですが、WCHAR
およびwsprinf
を使用します。
static WCHAR buffer[10];
static int pos = 0;
void printfcomma(const int &n) {
if (n < 0) {
wsprintf(buffer + pos, TEXT("-"));
pos = lstrlen(buffer);
printfcomma(-n);
return;
}
if (n < 1000) {
wsprintf(buffer + pos, TEXT("%d"), n);
pos = lstrlen(buffer);
return;
}
printfcomma(n / 1000);
wsprintf(buffer + pos, TEXT(",%03d"), n % 1000);
pos = lstrlen(buffer);
}
void my_sprintf(const int &n)
{
pos = 0;
printfcomma(n);
}