vlambda博客
学习文章列表

记一次.NET 与R语言交互

这是将近二年前,有一个项目使用.NET相关技术,但是项目中要将写好的R语言的代码,直接通过.NET调用,得到计算结果,遇到了这么多的坑,在此记下一些过程,文档太少,英文不好!

  • 如下为开发过程中可能出现的问题!

  • 相关博客介绍

我找了很多博客,在CSDN上的代码,基本都是重复的,也没有太多的介绍,一般情况都是官网直接翻译下来的,遇到一些问题时,根本查不到是什么原因。那么,遇到问题,怎么办?

1、看官网,里面有一些示例的链接:这个相当于示例代码库

R.NET是什么?

我的理解,R语言就像是SQL语言,用于数据计算,数据处理。R.Net这个类库就类似ADO.NET的技术,帮助我们实现.NET程序访问R语言代码,R.NET就相当于一个驱动程序。当然,严格意义上,我也不知道RDotNet是不是这样子,看他介绍,应该是起一R引擎。。。。(不懂.jpg)

使用RDotNet的步骤:

1、首先,你要看懂一些简单的R程序,就像,你不懂sql 语句,你怎么写ADO.NET程序一样 (安装R的环境,不要装最新的3.5版本,因为,R.NET并没有做到最新的版本,看他的github就知道了,我本地装的3.3.3,一点问题都没有。如果你安装3.5版本,会一直报一个空指针异常,反正我被坑了。)

2、将我上面所说的示例代码库下载下来,从最简单的代码开始看起。

如下为开发过程中可能出现的问题!

  • 而我遇到什么样的问题呢?我是如何解决的?
  • 还有哪些问题需要解决?
  • 这个类库还有哪些问题不能解决,他有哪些缺点?

回答上面的问题.

1.我遇到的问题。

1)、安装过新的R环境3.5导致一直报错,一点思路都没有。如何解决:看github的issue,看到有人提的问题,大意是指。R.NET不能支持到最新的R语言版本。

2)、一个简单的程序,在控制台上执行,是OK的。然而在ASP.NET MVC程序上一直不成功。如何解决:第一步:先把R的环境变量配置好,类似JDK的环境变量配置 找到自己的R语言位置,将32位和64位的环境都配置至path中

 ;C:\Program Files\R\R-3.3.3\bin\i386;C:\Program Files\R\R-3.3.3\bin\x64;

第二步,如何还有问题的话:比如一直报StackOverflowException的异常,建议你看他的issue:这个是他们的讨论原本我也遇到这个问题,但R的版本用了3.3,就没这个错了。

3)、当R的引擎正常结束后,调用 dispose后,下次调用 就会报错,那就不调用 dispose方法吧。那个作者说:There can only be one R engine in a process, and R itself is largely not thread-safe.

2.还有哪些问题需要解决?

  1. 管理员权限运行Rgui,并指定包安装的位置,防止安装到其他目录 此命令设置当前包安装的位置,
.libPaths("C:/Program Files/R/R-3.3.3/library")

此命令查看当前包会安装的位置

.libPaths()
  1. 如果有些包无法正常安装,如果能找到那个包,可直接复制其至目录C:/Program Files/R/R-3.3.3/library
  2. 运行和发布后的代码都应运行在64位机子及平台上。

3.这个类库还有哪些问题不能解决,他有哪些缺点?

  1. 不能兼容所有的R版本,目前只兼容到R3.3.3。
  2. 如下代码在本机windows10 专业版 中文版电脑上运行会乱码
 CharacterVector datFname = engine.CreateCharacter("我是中文");

我研究下代码,是这个类中的方法有问题InternalString  下的方法 StringFromNativeUtf8

       /// <summary>
       /// Convert utf8 to string
       /// </summary>
       /// <param name="utf8">utf8 to convert</param>

       public static string StringFromNativeUtf8(IntPtr utf8)
       {
           int len = 0;
           while (Marshal.ReadByte(utf8, len) != 0) ++len;
           byte[] buffer = new byte[len];
           Marshal.Copy(utf8, buffer, 0, buffer.Length);
           return Encoding.UTF8.GetString(buffer);
       }

我在我本地调试源码时,发现,如果包含中文时,采用Encoding.Default.GetString(buffer);转换,这里就可以正常转换,但这样子,就只支持中文和英文了。而且,好像在测试过程中,改成Default后,如下方法中包含中文,反而乱码了。

       string[] rownames = engine.GetSymbol("rownames").AsCharacter().ToArray();

所以我准备直接用最简单的方式 ,判断buffer的编码,如果不为UTF8,Convert为UTF8,然后返回,否则,这里就需要多此一举,因为,在测试过程发现,判断UTF8,不准确,明明不是UTF8,还是返回了UTF8,所以在这里需要判断,转换后的数据是否包含中文,如果包含,则直接返回,否则要从Default(根据系统的编码决定),转换为UTF8,就能返回中文 。

       /// <summary>
       /// Convert utf8 to string
       /// </summary>
       /// <param name="utf8">utf8 to convert</param>

       public static string StringFromNativeUtf8(IntPtr utf8)
       {
           int len = 0;
           while (Marshal.ReadByte(utf8, len) != 0) ++len;
           byte[] buffer = new byte[len];
           Marshal.Copy(utf8, buffer, 0, buffer.Length);
           Encoding encoding = GetType(buffer);
           if (encoding.Equals(Encoding.UTF8))
           {
               string r = Encoding.UTF8.GetString(buffer);
               if (System.Text.RegularExpressions.Regex.IsMatch(r, @"[\u4e00-\u9fbb]+$"))
               {
                   return r;
               }
               else
               {
                   byte[] newBuffer = Encoding.Convert(Encoding.Default, Encoding.UTF8, buffer);
                   return Encoding.UTF8.GetString(newBuffer);
               }
           }
           else
           {
               byte[] newBuffer = Encoding.Convert(encoding, Encoding.UTF8, buffer);
               return Encoding.UTF8.GetString(newBuffer);
           }
       }

代码已放到github

相关博客介绍

  • 用C#调用R语言开发.NET MVC Web服务 此文章中介绍了.NET启动控制台调用R代码,这个思路非常好,一开始对R语言了解甚少,所以还是使用R.NET来实现.NET与R语言的交互,反而耗时耗力。
  • 一键运行R脚本 这个文章让我了解到Rscript.exe这个程序的作用,以及执行方式。
  • c#调用R语言(原创翻译)

由于开发过程中,有个R程序运行时间非常长,最长可达2小时,所以只能以后台任务方式执行,但 RDotNET,一次只能运行一个,不执行完,其他的程序在WEB项目下无法正常运行,看了上面的文章,有了思路 :使用.NET的进程Process起一个cmd命令,类似 java配置好环境变量后可使用java,javac命令一样,配置到Path中后,可直接在cmd中使用如下命令

Rscript.exe "某目录下\test.R"  agruments 

其中R程序包要有双引号   多个参数使用空格分隔 ,如下为.NET下使用Process启动cmd命令,并执行Rscript.exe 命令启动R引擎,让其后台运行,运行结束后,才会往后执行。

    public void Execute()
        {
            List<string> arguments = new List<string>
            {
                参数
            };

            Process cmd = new Process
            {
                StartInfo =
                {
                    FileName = @"Rscript.exe",
                    WorkingDirectory = AppFolders.RSourceCodeFolder,//.R代码的位置 "E:\svn\CHNMed\CHNMed.Web\DataUsers\RSourceCode"
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    Arguments = " Test.r " + string.Join(" ", arguments),
                    CreateNoWindow = true,//不显示程序窗口
                }
            };
            cmd.Start();//启动程序
            var output = cmd.StandardOutput.ReadToEnd();
            var error = cmd.StandardError.ReadToEnd();
            cmd.WaitForExit();//等待控制台程序执行完成
   cmd.Close();//关闭该进程
            Logger.InfoFormat($"参数:{output}出错信息:{error}");
        }