2011-08-18 65 views
3

我正在Arduino上开发一个项目,它解析来自远程Web API的一些JSON数据,并在16x2 LCD上显示它。如何在Arduino上格式化长添加千位分隔符

我想格式化一个长解析TextFinder添加千位分隔符(逗号分隔符将罚款)。

简而言之,我该如何编写formatLong函数?

long longToBeFormatted = 32432423; 

formattedLong = formatLong(longToBeFormatted); //How to implement this? 

lcd.print(formattedLong) // formattedLong is a string 

回答

4

我不确定在Arduino上使用了哪些工具集。有时候库将支持非标准的“千人分组”标志 - 单引号字符是典型的扩展:

printf("%'ld",long_val); 

如果你的库不支持这一点,像下面这样可能会做:

#include <stddef.h> 
#include <string.h> 
#include <limits.h> 
#include <stdio.h> 
#include <assert.h> 

size_t strlcpy(char* dest, char const* src, size_t dest_size); 

size_t format_long(long x, char* buf, size_t bufsize) 
{ 
    // This code assumes 32-bit long, is that the 
    // case on Arduino? Modifying it to be able to 
    // handle 64-bit longs (or to not care) should be 
    // pretty straightforward if that's necessary. 

    char scratch[sizeof("-2,147,483,648")]; 
    char* p = scratch + sizeof(scratch); // Work from end of buffer 
    int neg = (x < 0); 

    // Handle a couple special cases 
    if (x == 0) { 
     return strlcpy(buf, "0", bufsize); 
    } 
    if (x == INT_MIN) { 
     // Lazy way of handling this special case 
     return strlcpy(buf, "-2,147,483,648", bufsize); 
    } 

    // Work with positive values from here on 
    if (x < 0) x = -x; 

    int group_counter = 3; 
    *(--p) = 0; // Null terminate the scratch buffer 

    while (x != 0) { 
     int digit = x % 10; 
     x = x/10; 

     assert(p != &scratch[0]); 
     *(--p) = ""[digit]; 

     if ((x != 0) && (--group_counter == 0)) { 
      assert(p != &scratch[0]); 
      *(--p) = ','; 
      group_counter = 3; 
     } 
    } 

    if (neg) { 
     assert(p != &scratch[0]); 
     *(--p) = '-'; 
    } 
    return strlcpy(buf, p, bufsize); 
} 


/* 
    A non-optimal strlcpy() implementation that helps copying string 
    without danger of buffer overflow. 

    This is provided just in case you don't have an implementation 
    so the code above will actually compile and run. 
*/ 
size_t strlcpy(char* dest, char const* src, size_t dest_size) 
{ 
    size_t len = strlen(src); 

    if (dest_size == 0) { 
     // nothing to copy - just return how long the buffer should be 
     // (note that the return value doens't include the null terminator) 
     return len; 
    } 

    size_t tocopy = (dest_size <= len) ? dest_size-1 : len; 

    memmove(dest, src, tocopy); 
    dest[tocopy] = 0; 

    return len; 
} 
+0

试试用'final [final_pos--] = buffer [buff_pos - ]'来代替感谢您的帮助。分组不起作用,我也用gcc 4.0在本地Mac上尝试过,但没有成功,你知道关于该标志的任何文档吗? 'format_long'函数很好用,不需要使用'strlcpy'本地函数。 Arduino上的长类型是32位的,所以你的功能非常适合。还有一个问题:为什么'format_long'函数需要'bufsize'参数? – systempuntoout

+0

我很喜欢让调用者指定缓冲区大小,所以函数可以避免超出缓冲区的末端(这就是为什么我使用'strlcpy()')。另一种方法是让函数动态分配所需大小的缓冲区并将其返回给调用者。但是在涉及像arduino这样的小设备的项目中,动态内存通常不被使用。当然这取决于你的具体项目的要求。 –

+1

在这里的GCC(实际上是libc)文档中提到了千分组标志:http://www.gnu.org/s/hello/manual/libc/Integer-Conversions.html#Integer-Conversions(但没有非常详细)。当然,你的arduino目标可能没有完整的'libc'实现。 –

1

也许不是最好的算法,但这里的一个实例(标准C):

char* formatLong(long toBeFormatted) 
{ 
    // Get the string representation as is 
    char* buffer = (char*) malloc(sizeof(long)); 
    ltoa(toBeFormatted, buffer, 10); 

    // Calculate how much commas there will be 
    unsigned int buff_length = strlen(buffer); 
    unsigned int num_commas = buff_length/3; 
    unsigned int digits_left = buff_length % 3; 
    if (digits_left == 0) 
    { 
     num_commas--; 
    } 

    // Allocate space for final string representation 
    unsigned int final_length = buff_length + num_commas + 1; 
    char* final = (char*) malloc(final_length); 
    memset(final, 0, final_length); 

    // Parse strings from last to first to count positions 
    int final_pos = final_length - 2; 
    int buff_pos = buff_length - 1; 
    int i = 0; 
    while(final_pos >= 0) 
    { 
     final[final_pos--] = buffer[buff_pos--]; 
     i++; 
     if (i % 3 == 0) 
     { 
      final[final_pos--] = ','; 
     } 
    } 

    // Free obsolete memory and return buffer 
    free(buffer); 
    return final; 
} 
+0

如果有人使用更短/更快的算法,请发布它! – m0skit0

+0

我试过了,它似乎以某种方式工作。最后一位数字有问题;通过你的函数打印15195返回15,19ç,最后一个字符肯定是虚假的。 – systempuntoout

+0

在x86中正常工作。你可以用_memcpy()_... – m0skit0

0

你可以尝试以下方法:

std::string formatLong(long l) { 
    int power = 0; 
    while (l/(pow(1000, power)) { power += 1; } 

    power -= 1; 

    std::stringstream s; 
    while (power) { 
     s << l/pow(1000, power); 
     s << ","; 
     l % (pow(1000, power)); 
     power -= 1; 
    } 
    return s->str(); 
} 

的代码是从臀部射击,但这个想法应该起作用。

相关问题