我的代码是要处理这样的字符串
$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
程序要读入多行这样的字符串,依据","分割字符串
然后判断0号字段是否为"$GPRMC",2号字段是否为“A",并且验证校验和,即从字符'$'到''中间的所有字符的异或为''后面的两个字符(这两个字符为十六进制表示)
最后将最后一个符合要求的字符串的1号字段前6个字符(即 uc t时间)转化为 bjt 时间
在最后一步,我遇到这样一个问题,我开了一个大小为7的字符数组,将转化后的 bjt 时间用 strcpy()填充到字符数组中却得到乱码,请问是怎么回事呢?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* str_sub( char str[], int start, int end );
void str_split( char* words[], char str[], char* delim );
int chk_sum( char sen[] );
int hex_to_dec( char *s );
int chk_val( char sen[] );
void print_bjt( char uct[] );
int is_vaild( char *words[], int chksum, int chkval );
int main()
{
/*char str[] = "$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50";*/
char sen[255];
char uct[7];
scanf ( "%s", sen );
while ( strcmp( sen, "END" ) != 0 )
{
char *words[17];
int chksum = chk_sum( sen );
int chkval = chk_val( sen );
printf ( "chksum=%d chkval=%d\n", chksum, chkval );
str_split( words, sen, "," );
{
printf ( "word[1]=%s\n", words[0] );
printf ( "word[2]=%s\n", words[1] );
printf ( "%s\n", sen );
}
if ( is_vaild( words, chksum, chkval ) == 1 )
{
printf ("%s\n", str_sub( words[1], 0, 6 ) );
strncpy( uct, str_sub( words[1], 0, 6 ), 6 );
printf ("%s\n", uct );
}
scanf ( "%s", sen );
}
printf ("%s\n", uct );
print_bjt( uct );
return 0;
}
char* str_sub( char str[], int start, int end )
{
char new_str[end - start + 1];
char *result = new_str;
for ( int i = start, j = 0; i < end - start; i++, j++ )
{
new_str[j] = str[i];
}
result[end-start] = '\0';
return result;
}
void str_split( char* words[], char str[], char* delim )
{
int i = 0;
char *buf = str;
while ( (words[i] = strtok( buf, delim ) ) != NULL )
{
i++;
buf = NULL;
}
}
int chk_sum( char sen[] )
{
char *p = NULL;
p = strchr ( sen, '$' ) + 1;
int xor = *p++;
while ( *p != '*' )
{
xor ^= *p;
p++;
}
xor %= 65536;
return xor;
}
int chk_val( char sen[] )
{
char *p = NULL;
p = strchr( sen, '*' ) + 1;
int checkval = hex_to_dec( p );
return checkval;
}
int hex_to_dec( char *s )
{
int n = 0;
int i = 0;
for (i = 0; s[i] != '\0'; i++)
{
if (s[i] >= '0' && s[i] <= '9')
n = n * 16 + s[i] - '0';
if (s[i] >= 'a' && s[i] <= 'f')
n = n * 16 + s[i] - 'a' + 10;
if (s[i] >= 'A' && s[i] <= 'F')
n = n * 16 + s[i] - 'A' + 10;
}
return n;
}
int is_vaild( char *words[], int chksum, int chkval )
{
int ret = 0;
if ( strcmp( words[0], "$GPRMC" ) == 0 &&
strcmp( words[2], "A" ) == 0 &&
chksum == chkval )
{
ret = 1;
}
return ret;
}
void print_bjt( char uct[] )
{
int uct_h1 = ( uct[0] - '0' ) * 10;
int uct_h2 = ( uct[1] - '0' );
int uct_hh = uct_h1 + uct_h2;
int bjt_hh = ( uct_hh + 8 ) % 24;
printf ( "%2d:%c%c:%c%c", bjt_hh, uct[2], uct[3], uct[4], uct[5] );
}
1
lx19891024 2015-01-21 16:37:58 +08:00
虽然我没有看你的代码,不过下次好歹程序运行到你出错那前面打个断点,我看你已经输出%s成功了是吧(printf ("%s\n", uct );
),那么你肯定没搞清楚字符串转换为整数是什么意思,用atoi函数。把数据类型弄一样了再运算。 不然你的字符'3'*10是什么意思 程序必然将'3'先转化为ascii码再做乘法,跟你想要达到效果是大相径庭的。简单的来说9+1=10. '9'+1=多少呢? 呵呵明白了吧 |
2
fliar 2015-01-21 19:30:11 +08:00
沒編過
char new_str[end - start + 1]; 你如果要申請變長char 數組,要用new 然後記得用完刪掉 |
3
fliar 2015-01-21 19:45:37 +08:00
你的另一個問題是strncpy的使用,你複製一個長度7的字串(0-6),但是只複製前6個沒有/0,所以你會看到亂碼
我簡單改了一下你的代碼,這份代碼可以運行但不代表正確(比如我new 出來數組沒刪),只是說明問題在哪裡 指針的問題你還要再研究,寫太多就長了 #include <stdio.h> #include <string.h> #include <stdlib.h> char* str_sub( char str[], int start, int end ); void str_split( char* words[], char str[], char* delim ); int chk_sum( char sen[] ); int hex_to_dec( char *s ); int chk_val( char sen[] ); void print_bjt( char uct[] ); int is_vaild( char *words[], int chksum, int chkval ); int main() { char str[] = "$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50"; char sen[255]; char uct[7]; memcpy(sen, str, sizeof str); //scanf ( "%s", sen ); while ( strcmp( sen, "END" ) != 0 ) { char *words[17]; int chksum = chk_sum( sen ); int chkval = chk_val( sen ); printf ( "chksum=%d chkval=%d\n", chksum, chkval ); str_split( words, sen, "," ); { printf ( "word[1]=%s\n", words[0] ); printf ( "word[2]=%s\n", words[1] ); printf ( "%s\n", sen ); } if ( is_vaild( words, chksum, chkval ) == 1 ) { printf ("%s\n", str_sub( words[1], 0, 6 ) ); strncpy( uct, str_sub( words[1], 0, 6 ), 7 ); printf ("%s\n", uct ); } scanf ( "%s", sen ); } printf ("%s\n", uct ); print_bjt( uct ); return 0; } char* str_sub( char str[], int start, int end ) { char* new_str = new char[end - start + 1]; char *result = new_str; for ( int i = start, j = 0; i < end - start; i++, j++ ) { new_str[j] = str[i]; } result[end-start] = '\0'; return result; } void str_split( char* words[], char str[], char* delim ) { int i = 0; char *buf = str; while ( (words[i] = strtok( buf, delim ) ) != NULL ) { i++; buf = NULL; } } int chk_sum( char sen[] ) { char *p = NULL; p = strchr ( sen, '$' ) + 1; int xor = *p++; while ( *p != '*' ) { xor ^= *p; p++; } xor %= 65536; return xor; } int chk_val( char sen[] ) { char *p = NULL; p = strchr( sen, '*' ) + 1; int checkval = hex_to_dec( p ); return checkval; } int hex_to_dec( char *s ) { int n = 0; int i = 0; for (i = 0; s[i] != '\0'; i++) { if (s[i] >= '0' && s[i] <= '9') n = n * 16 + s[i] - '0'; if (s[i] >= 'a' && s[i] <= 'f') n = n * 16 + s[i] - 'a' + 10; if (s[i] >= 'A' && s[i] <= 'F') n = n * 16 + s[i] - 'A' + 10; } return n; } int is_vaild( char *words[], int chksum, int chkval ) { int ret = 0; if ( strcmp( words[0], "$GPRMC" ) == 0 && strcmp( words[2], "A" ) == 0 && chksum == chkval ) { ret = 1; } return ret; } void print_bjt( char uct[] ) { int uct_h1 = ( uct[0] - '0' ) * 10; int uct_h2 = ( uct[1] - '0' ); int uct_hh = uct_h1 + uct_h2; int bjt_hh = ( uct_hh + 8 ) % 24; printf ( "%2d:%c%c:%c%c", bjt_hh, uct[2], uct[3], uct[4], uct[5] ); } |
4
zeroday OP @lx19891024 就是输出 uct 是一个乱码,相不明白怎么会是乱码呢?
|
5
zeroday OP |
6
davidjqq19 2015-01-21 21:55:55 +08:00
我这里试了下跑出来是正常结果,没乱码。
[root@localhost test]# gcc test.c -std=c99 [root@localhost test]# ./a.out $GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50 chksum=80 chkval=80 word[1]=$GPRMC word[2]=024813.640 $GPRMC 024813 024813 END 024813 10:48:13[root@localhost test]# |
7
zeroday OP @davidjqq19 奇怪,zsh shell 和 bash shell 都试了,还是乱码
➜ ~ gcc test.c -std=c99 ➜ ~ ./a.out $GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50 chksum=80 chkval=80 word[1]=$GPRMC word[2]=024813.640 $GPRMC 024813 ?X? END ?X? -1:?X:?⏎ |
8
1423 2015-01-21 22:57:04 +08:00
uct 用之前清空试试?
|
10
kingcos 2015-01-22 00:43:23 +08:00 via Android
。。。为毛学完C表示看这个还是不懂。。。
看来我连初学都不算啊。。。 |
11
besto 2015-01-22 00:54:19 +08:00 1
@fliar 这是c99的code,需要在gcc -std=C99编译。
LZ我给你提个醒, 这算是C的一个坑。在函数内部使用数组,出了函数,这个数组会被释放掉的。必须malloc或是使用static变量,这里用了变长,不能用static了,只能malloc了。 贴个代码,你感受下: #include <stdio.h> char* wrong_fun(){ char local_array[]="12312321321324343543534"; char *will_be_delete = local_array; return will_be_delete; } int main(){ printf("%s\n", wrong_fun()); } 再试试:static char local_array[]="12312321321324343543534"; @zeroday @1423 @davidjqq19 |
12
NeoAtlantis 2015-01-22 00:59:14 +08:00
跑下题,我觉得这个问题可以试试用python解决= =
|
13
canautumn 2015-01-22 07:11:00 +08:00 1
@zeroday @davidjqq19 一点也不奇怪。楼上说的很清楚了,你取了一个local variable的地址传回去了,函数返回后local variable都被释放了,这个地址就变成幽灵了,它指向的地址是一个薛定谔的猫,有两种状态,老值可能还存在,也可能不存在了。这个传回的指针实际上是非法的。你可能能得到正确的结果,但多半得不到正确的结果。不信你多运行几次可能显示都不一样。
建议找一本好的C语言教材扎扎实实从头学一下。另外,能用C++就不要再用C了,C的内存管理是个大坑。用现代C++能避免很多问题。顺便说一下这种看似随机的错误,多半是非法指针搞的。 |
14
canautumn 2015-01-22 07:18:48 +08:00
楼上还有混合C和C++的。在C99下用malloc的话,你的那行
char new_str[end - start + 1]; 改成 char *new_str = malloc((end - start + 1) * sizeof(char)); 就没乱码了。然后在调用这个函数之后按理说还应该free。这是最小化改动现有代码的方法,只是太不优雅了。 |
15
zeroday OP @kingcos 没,是我写的一点都不好。
我简单介绍一下我的代码。 char* str_sub( char str[], int start, int end ); 切割出传入的字符串,从下标为 start 到 end为止。 思路是,创建一个 end-start+1 长度的字符数组,循环字符串,将字符串中下标为 start 到 end 的字符 传入到数组中,最后返回这个数组,即切割好的字符串 void str_split( char* words[], char str[], char* delim ); 将字符串根据指定的字符切割成,字符串数组。 这里我需要将题目中传入的句子根据逗号切割成字段,就用到它。它的实现,用到 strtok() 切割字符串,用法我是参考文档的,我做的就是将切割好的字符串,存到字符串数组中,字符串数组我定义为 char ** 不知道合不合适。 int chk_sum( char sen[] ); 计算校验和 根据题目要求计算 '$'到'*'的所有字符的异或值 int hex_to_dec( char *s ); 将十六进制转化为十进制 int chk_val( char sen[] ); 得到字符串最后一个字段'*'字符后面的两个数字 void print_bjt( char uct[] ); 根据传入的 uct 字符串,打印 bjt ,格式为 hh:mm:ss int is_vaild( char *words[], int chksum, int chkval ); 根据题目要求验证,0号字段是否为"$GPRMC",2号字段是否为“A",并且验证校验和,即从字符'$'到''中间的所有字符的异或为''后面的两个字符 |
16
davidjqq19 2015-01-22 09:45:12 +08:00 1
楼上诸位说得对,问题出在str_sub()函数,result指针指向的new_str[]数组位于栈区,函数执行完毕就被释放了,所以得到的结果不可预料,应该改为malloc动态分配。
|
17
zeroday OP @NeoAtlantis 一开始我用 Java 实现并且通过,只是课程要求必须用 C 实现,准备改写为 C 时发现,C 中并没有 Java的处理字符串的函数,然后自己写函数实现,但是指针字符串没过关,就遇到现在这种情况了。C 处理字符串感觉没有 Java 方便。
|
18
zeroday OP @besto
@canautumn @davidjqq19 谢谢了,学会了 malloc 的用法。发现 C 中的指针真的很难,昨晚我在调式代码找错误时,发现如果输入的字符串不是题目的格式,比如 ”dasdas,dasdas,asdasd", 程序就提示 terminated by signal SIGSEGV (Address boundary error) |
19
fliar 2015-01-22 12:12:50 +08:00
@zeroday 我代碼有寫,你比下差異
strncpy( uct, str_sub( words[1], 0, 6 ), 7 ); |
22
canautumn 2015-01-22 13:38:52 +08:00 1
@zeroday 之前没仔细看你的代码。现在发现你没必要自己写一个str_sub。你想想,strncpy不就是str_sub吗?你把
printf ("%s\n", str_sub( words[1], 0, 6 ) ); strncpy( uct, str_sub( words[1], 0, 6 ), 6 ); 这两行换成 strncpy( uct, words[1], 6 ); 一样能跑通,还没有你出现的内存的问题。 另外楼上有提到,你的uct的最后一个字符串结束符\0没有处理。最好的办法其实是在初始化uct时把uct[6]初始化为0。数组在创建时是不自动初始化的,里边的值可能是任意值,只不过程序刚开始很可能是0,但是不初始化是不安全的。应该养成好习惯。万一不是0,你的uct就可能出问题了。 |