使用 c++2b 编码不可知的解析

如何解决使用 c++2b 编码不可知的解析

有时我必须解析各种编码的文本文件, 我想知道即将出台的标准是否会为此带来一些工具 因为我对我目前的解决方案不是很满意。 我什至不确定这是否是正确的方法,但是 我定义了一个函子模板来从流中提取一个字符:

#include <string>
#include <istream> // 'std::istream'

/////////////////////////////////////////////////////////////////////////////
// Generic implementation (couldn't resist to put one)
template<bool LE,typename T> class ReadChar
{
 public:
    std::istream& operator()(T& c,std::istream& in)
       {
        in.read(buf,bufsiz);
        //const std::streamsize n_read = in ? bufsiz : in.gcount();
        if(!in)
           {// Could not real all bytes
            c = std::char_traits<T>::eof();
           }
        else if constexpr (LE)
           {// Little endian
            c = buf[0];
            for(int i=1; i<bufsiz; ++i) c |= buf[i] << (8*i);
           }
        else
           {// Big endian
            const std::size_t imax = bufsiz-1;
            for(std::size_t i=0; i<imax; ++i) c |= buf[i] << (8*(imax-i));
            c |= buf[imax];
           }
        return in;
       }

 private:
    static constexpr std::size_t bufsiz = sizeof(T);
    unsigned char buf[bufsiz];
};

/////////////////////////////////////////////////////////////////////////////
// Partial specialization for 32bit chars
template<bool LE> class ReadChar<LE,char32_t>
{
 public:
    std::istream& operator()(char32_t& c,4);
        if constexpr (LE) c = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); // Little endian
        else              c = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; // Big endian
        return in;
       }

 private:
    char buf[4];
};

/////////////////////////////////////////////////////////////////////////////
// Partial specialization for 16bit chars
template<bool LE> class ReadChar<LE,char16_t>
{
 public:
    std::istream& operator()(char16_t& c,2);
        if constexpr (LE) c = buf[0] | (buf[1] << 8); // Little endian
        else              c = (buf[0] << 8) | buf[1]; // Big endian
        return in;
       }

 private:
    char buf[2];
};

/////////////////////////////////////////////////////////////////////////////
// Specialization for 8bit chars
template<> class ReadChar<false,char>
{
 public:
    std::istream& operator()(char& c,std::istream& in)
       {
        return in.get(c);
       }
};

我使用ReadChar来实现解析功能:

template<typename T,bool LE> void parse(std::istream& fin)
{
    ReadChar<LE,T> get;
    T c;
    while( get(c,fin) )
       {
        if(c==static_cast<T>('a')) {/* ... */} // Ugly comparison of T with a char literal
       }
}

当我需要与字符文字进行比较时,难看的部分是 static_cast

然后我将 parse 与这个丑陋的样板代码一起使用:

#include <fstream> // 'std::ifstream'
std::ifstream fin("/path/to/file",std::ios::binary);
auto bom = check_bom(fin); // 'check_bom' function is quite trivial
     if( bom.is_empty()  )  parse<char>(fin);
else if( bom.is_utf8() )    parse<char>(fin); // In my case there's no need to handle multi-byte chars
else if( bom.is_utf16le() ) parse<char16_t,true>(fin);
else if( bom.is_utf16be() ) parse<char16_t,false>(fin);
else if( bom.is_utf32le() ) parse<char32_t,true>(fin);
else if( bom.is_utf32be() ) parse<char32_t,false>(fin);
else                        throw std::runtime_error("Unrecognized BOM");

现在,这个解决方案有一些怪癖(不能在 parse 中直接使用字符串文字) 我的问题是是否有其他方法可以解决这个问题, 也许使用我忽略的现有或即将推出的标准设施。

解决方法

中,我们获得了类型安全联合。这些可以与 std::visit 一起用于在运行时和编译时状态之间进行映射。

template<auto x>
using constant_t = std::integral_constant<std::decay_t<decltype(x)>,x>;
template<auto x>
constexpr constant_t<x> constant = {};

template<auto...Xs>
using variant_enum_t = std::variant< constant_t<Xs>... >;

enum class EBom {
  None,utf8,utf16le,utf16be,utf32le,utf32be,count,};
// you could use the existence of EBom::count and the
// assumption of contiguous indexes to automate this as well:
using VEBom = variant_enum< EBom::None,EBom::utf8,EBom::utf16le,EBom::utf16be,EBom::utf32le,EBom::utf32be >;

template<std::size_t...Is>
constexpr VEBom make_ve_bom( EBom bom,std::index_sequence<Is...> ) {
  static constexpr VEBom retvals[] = {
    constant<static_cast<EBom>(Is)>...
  };
  return retvals[ static_cast<std::size_t>(bom) ];
}
constexpr VEBom make_ve_bom( EBom bom ) {
  return make_ve_bom( bom,std::make_index_sequence< static_cast<std::size_t>(EBom::count) >{} );
}

现在,使用运行时 EBom 值,我们可以生成 VEBom

使用 VEBom 我们可以在编译时获得类型。假设您具有以下特征:

template<EBom>
constexpr boom bom_is_bigendian_v = ???;
template<EBom>
using bom_chartype_t = ???;

您现在可以编写如下代码:

std::visit( vebom,[&](auto bom) {
  bom_chartype_t<bom> next = ???;
  if constexpr (bom_is_bigendian_v<bom>) {
    // swizzle
  }

} );

您的非 DRY 代码

template<bool LE,class char_t> class ReadChar {
public:
  std::istream& operator()(char_t& c,std::istream& in)
  {
    in.read(buf,sizeof(char_t));
    c = buf[0] | (buf[1] << 8);
    if constexpr(!LE)
      reverse_bytes(&c);
    return in;
  }
private:
  char buf[sizeof(char_t)];
};

通过简单的重写变得 DRY。

你的样板变成:

std::ifstream fin("/path/to/file",std::ios::binary);
auto bom = check_bom(fin); // 'check_bom' function is quite trivial
if (bom.invalid())
  throw std::runtime_error("Unrecognized BOM");

auto vebom = make_ve_bom( bom.getEnum() );
std:visit( vebom,[&]( auto ebom ) {
  parse<bom_chartype_t<ebom>,!bom_is_bigendian_v<ebom>>( fin );
});

魔法是在别处完成的。

这里的神奇之处在于 std::variant 拥有一堆 integral_constants,每个 std::variant 都是无状态并且知道(在其类型中)它的值是什么。

所以 std::visit 中的唯一状态是它包含的无状态枚举值。

std::integral_constant 继续使用 std::variant 中的无状态 std::integral_constant 调用传入的 lambda。在那个 lambda 中,我们可以使用它的值作为编译时常量,就像我们使用任何其他 std::variant 一样。

EBom 的运行时状态实际上是 EBom 的值,因为我们如何设置它,因此将 VEBom 转换为 std::visit 实际上是复制数量超过(所以,免费)。神奇之处在于 template<class Enum,std::size_t...Is,class VEnum=variant_enum< constant_t<static_cast<Enum>(Is)>... >> constexpr VEnum make_venum( Enum e,std::index_sequence<Is...> ) { static constexpr VEnum retvals[] = { constant<static_cast<Enum>(Is)>... }; return retvals[ static_cast<std::size_t>(e) ]; } template<class Enum> constexpr auto make_venum( Enum e ) { return make_venum( e,std::make_index_sequence< static_cast<std::size_t>(Enum::count) >{} ); } template<class Enum> using venum_t = decltype(make_venum( static_cast<Enum>(0) )); ,它会自动编写 switch 语句并将每种可能性的编译时间(积分常数)值注入到您的代码中。

这些都不是。其中大部分是 ,我可能也在其中使用了 功能。

上面的代码没有编译,只是写的。它可能包含错别字,但技术是合理的。

--

我们可以自动制作变体类型:

VEBom

现在我们的 using VEBom = venum_t<EBom>; 只是:

let config = {
    user: 'user',password: 'password',server: 'server',database: 'database',"options":{
        instanceName: 'instanceName',"encrypt":true,"enableArithAbort":true,"trustServerCertificate": true,}
};
module.exports=config;

无论如何,修正了错别字的live example

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res