C++字符相同的输入给出不同的输出

C++ char same input giving different outputs

本文关键字:输出 输入 字符 C++      更新时间:2023-10-16

我有一个cpp类,它实现了Netezza用户定义的函数(此处为文档)。它接受一个参数,该参数将是某种日期格式的字符串,并将其转换为YYYYMMDD格式。如果它不是一个有效的日期,它将返回"99991231"。每当我在一些表上运行代码时,对于相同的输入,每次都会得到不同的输出。我想我没有看到一些记忆问题。

从逻辑上讲,我们将char数组retval设置为等于date命令的输出。如果它给出一个空输出,我们将设置为"99991231"。然后,我们将一个temp-char数组设置为retval的前9个字节(最后一个是null终止符)。然后,我们将其memcpy到ret->data(我们必须返回的结构的字符ptr)中。

#include <stdarg.h>
#include <string.h>
#include "udxinc.h"
#include "udxhelpers.h"
using namespace nz::udx_ver2;
class Dateconvert: public Udf
{
public:
Dateconvert(UdxInit *pInit) : Udf(pInit){}
~Dateconvert(){}
static Udf* instantiate(UdxInit *pInit);
virtual ReturnValue evaluate()
{
StringReturn* ret = stringReturnInfo();
StringArg *str;
str = stringArg(0);
int lengths = str->length;
char *datas = str->data;
string tempData = datas;
string shell_arg = tempData;
shell_arg = "'" + shell_arg + "'";
string cmd="date -d " + shell_arg +  " +%Y%m%d 2>/dev/null";
FILE *ls = popen(cmd.c_str(), "r");
char retval[100];
retval[0]='n';
fgets(retval, sizeof(retval), ls);
if(!isdigit(retval[0]))
{
strcpy(retval,"99991231");
}
pclose(ls);
char temp1[9];
memcpy(temp1, retval, 8);
temp1[8]='';
ret->size = 9;
memcpy(ret->data, temp1, 9);
NZ_UDX_RETURN_STRING(ret);
}
};

Udf* Dateconvert::instantiate(UdxInit *pInit)
{
return new Dateconvert(pInit);
}

当我在Netezza中对一个不同的值运行UDF时,它会给我预期的输出。然而,当我在多个列上运行它时,输出有时是正确的,有时是错误的,看起来是随机的。我认为这一定是内部内存问题。示例:

input          output
1) 8/11/2014      20140811
2) 8/11/2014      20140811
Fri 10/17/14   20141017
3) 8/11/2014      99991231
Fri 10/17/14   20141017
4) 8/11/2014      20140811
Fri 10/17/14   20141017
5) 8/11/2014      20140811
Fri 10/17/14   20141017
9-Nov-12       20121109
6) 8/11/2014      20140811
Fri 10/17/14   20141017
9-Nov-12       01241109 (what?)
7) 8/11/2014      99991231
Fri 10/17/14   20141017
9-Nov-12       20121109

只要函数只有一个调用,它就会返回正确的答案。当它被多次调用时,问题就出现了,我不明白。为什么会有什么遗留下来?在evaluate函数结束时,将返回值大小从9更改为8并不能解决问题。

这是调用函数的格式:

select a.val1, DATECONVERT(a.val1)
from
(
select '8/11/2014' as val1 from calendar
union
select 'Fri 10/17/14' as val1 from calendar
union
select '9-Nov-12' as val1 from calendar
) a

并为UDF:编译命令

nzudxcompile /export/home/nz/dateconvert.cpp -o dateconvert.o --sig "Dateconvert(VARCHAR(200))" --version 2 --return "VARCHAR(200)" --class Dateconvert --user user1 --pw mypw  --db mydb

切入正题,这里的问题在于如何分配tempData。

StringReturn* ret = stringReturnInfo();
StringArg *str;
str = stringArg(0);
int lengths = str->length;
char *datas = str->data;
string tempData = datas;

StringArg不存储以NUL结尾的字符串,而是提供长度,并希望您自己管理。

select a.val1, ADMIN.DATECONVERT(a.val1)
from
(
select '09-Nov-12'::varchar(20) as val1 
union all
select '9-Nov-12'::varchar(20) as val1 
) a;
VAL1    | DATECONVERT 
-----------+-------------
09-Nov-12 | 20121109
9-Nov-12  | 01221109
(2 rows)

在本例中,发生的情况是,当第二个较短的字符串被分配给tempData时,较长的第一个字符串仍有一个字符挂在内存中。最后挂着的"2"像这样添加:

09-Nov-12
9-Nov-122

其中每一个都是date的有效输入,这很好地解释了您所看到的输出。

$ date -d 09-Nov-12 +%Y%m%d
20121109
$ date -d 09-Nov-122 +%Y%m%d
01221109 

将作业更改为使用该长度,这样就可以避免问题。

//string tempData = datas;
string tempData(datas, datas+lengths);

然后你得到了预期的输出:

select a.val1, ADMIN.DATECONVERT(a.val1)
from
(
select '09-Nov-12'::varchar(20) as val1 
union all
select '9-Nov-12'::varchar(20) as val1 
) a;
VAL1    | DATECONVERT 
-----------+-------------
09-Nov-12 | 20121109
9-Nov-12  | 20121109
(2 rows)

话虽如此,我不知道您在这个UDF中采用的总体方法是否可行。正如我在上面运行的那样,这些行是在主机上生成的,因为它们在SQL中是硬编码的,并且日期在主机上肯定可用。然而,您不能期望在MPP后端(我们通常称之为SPU)上运行的代码具有与主机上相同的linux实用程序可用性,或者如果它们存在,则它们具有相同的功能。

如果我将日期移动到一个实际的表中,UDF将在SPU上对其进行操作,它将给我错误的输出,因为SPU映像上的date命令与主机的命令有很大不同,并且根本不理解这种输入格式。

select a.col1, admin.DATECONVERT(a.col1) from calendar a;
COL1    | DATECONVERT 
-----------+-------------
09-Nov-12 | 99991231
9-Nov-12  | 99991231
(2 rows)