`
lzkyo
  • 浏览: 466797 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

文件读取三

    博客分类:
  • Java
阅读更多
1、描述:流是字节数据或字符数据序列。Java采用输入流对象和输出流对象来支持程序对数据的输入和输出。输入流对象提供了数据从源点流向程序的管道,程序可以从输入流对象读取数据;输出流对象提供了数据从程序流向终点的管道,程序通过该管道把数据写到终点。所有的关于输入/输出的类都包含在java.io的包中。
2、File类:它主要关心的是文件的具体属性,而非内容,定义了许多方法,实现对文件的创建、删除等操作。
code:
import java.io.*;
public class Test
{
 public static void main(String args[])throws Exception
 {
  File file1=new File("w1.txt");//在当前目录下
  file1.createNewFile();//得到文件w1.txt
  file1.mkdir();//得到目录w1.txt
  File file2=new File("D:\\javaprogram\\text\\w2.txt");//指定目录
  file2.createNewFile();//得到文件w2.txt
  //用静态字段separator获得系统分隔符,保证程序通用性
  File fDir=new File(File.separator);//作为字符是'\',作为File对象为当前根目录
  String fStr="javaprogram"+File.separator+"text"+File.separator+"w3.txt";
  File file3=new File(fDir,fStr);
  file3.createNewFile();//得到文件w3.txt
  file1.delete();
  file2.delete();
  file3.delete();
  //delete()方法删除文件,调用即删除,而deleteOnExit()是在JVM终止时才删除
  //这样就得以建立临时文件以存储临时数据,临时文件保存在临时文件夹
  //要找临时文件夹,请查看环境变量temp的设置
  for(int i=0;i<5;i++)
  {
   File file=File.createTempFile("wang",".temp");
   file.deleteOnExit();
  }
  Thread.sleep(3000);
  //下面的一段程序将实现,打印指定目录下的.java文件的信息
  File f=new File("d:\\javaprogram");
  if(f.exists())//判断文件是否存在
  {
   if(f.isDirectory())//判断文件是目录还是标准文件
   {
    //调用带参数的listFiles()方法返回满足特定过虑器的
    //此抽象路径名所表示目录中的文件和目录的抽象路径名数组
    File[] fname=f.listFiles(new FilenameFilter()
    {
     //匿名类实现接口FilenameFileter的唯一方法
     public boolean accept(File dir, String name) 
     {
      return name.indexOf(".java")!=-1;
     }
    });
    for(int i=0;i<fname.length;i++)
    {
     System.out.println(fname[i]);
    }
   }
  }
  else
  {
   System.out.println("文件夹不存在.");
  }
 }
}

3、字节流:
程序运行中常的I/O操作包括向标准设备输入输出数据和文件输入输出。对于前者,java定义了三个直接使用的流对象:System.err(标准错误输出)、System.out(标准输出)和System.in(标准输入);对于后者,可以使用FileOutputStream和FileInputStream。
code:
import java.io.*;
public class Test
{
 static final String file1="D:\\javaprogram\\w1.txt";
 static final String file2="E:\\database\\w2.txt";
 static final String file3="E:\\wmpub\\w3.txt";
 public static void main(String args[])throws IOException
 {
  /*//关于System.in
  int data;
  while ((data=System.in.read())!=-1)//Ctrl+c结束输入
  {
   System.out.write(data);
  }*/
  //下面的程序段用以向file1文件写入,把其内容的一部分复制到file2
  //再把file2文件完整地复制到file3,并打印出来
  FileOutputStream fos1=new FileOutputStream(file1);
  FileOutputStream fos2=new FileOutputStream(file2);
  FileOutputStream fos3=new FileOutputStream(file3);
  fos1.write("今天是2008年8月3号,离北京奥运会还有5天,心里非常激动啊.".getBytes());
  fos1.close();
  FileInputStream fis1=new FileInputStream(file1);
  fis1.skip(19);//跳过19个字节
  byte[] buf=new byte[fis1.available()];
  fis1.read(buf);
  fis1.close();
  System.out.println(new String(buf));
  fos2.write(buf);
  fos2.close();
  FileInputStream fis2=new FileInputStream(file2);
  while(fis2.available()>0)
  {
   byte[] b=new byte[fis2.available()];
   int let=fis2.read(b);
   if(let==-1)break;
   fos3.write(b,0,let);
  }
  System.out.println("复制成功!");
  fis2.close();
  fos3.close();
  //以下程序段实现了一个图像文件的复制
  FileInputStream a=new FileInputStream("4.jpg");
  FileOutputStream b=new FileOutputStream("3.jpg");
  byte c[]=new byte[a.available()];
  a.read(c);
  b.write(c);
  a.close();
  b.close();
 }
} 

4、字符流(FileWriter and FileReader)

import java.io.*;
public class Test
{
 public static void main(String args[])
 {
  //本程序完成从控制台读入文件名,并实现复制
  int length;
  char buf[]=new char[100];
  try
  {
   FileReader in=new FileReader(args[0]);
   FileWriter out=new FileWriter(args[1]);
   length=in.read(buf);
   while(length!=-1)
   {
    out.write(buf,0,length);
    //字符流读取通过read()的返回值判断是否读到文件末尾
    //我刚才忘记加这条语句,文件被一直写,都死机了,重启后一看,写了2.6G
    length=in.read(buf);
   }
   in.close();
   out.close();
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }
} 
5、过滤流之一
    Java利用过滤流可以在读写数据的同时对数据进行处理,以达到性能的改善,提高程序执行效率。将过滤流和某个输入流或输出流(节点流)连接。连接是通过在过滤流的构造方法中指定入口参数——节点流来实现的。
  §BufferedInputStream:
                        FileInputStream fis=new FileInputStream("w1.txt");
                        BufferedInputStream bis=new BufferedInputStream(fis);
                        byte[] buf=new byte[100];
                        int len=bis.read(buf,0,len);
                        System.out.println(new String(buf,0,len));
                        bis.close();
        §BufferedOutputStream:
                        FileOutStream fos=new FileOutputStream("w2.txt");
                        BufferedOutputStream bos=new BufferedOutputStream(bos);
                        bos.write("好好学习,天天向上".getBytes());
                        bos.flush();
                        bos.close();
    也可以是匿名创建:如:BufferedOutputStream bos=new BufferedOutputStream(new FileInputStream("w2.txt")); 
     BufferedReader和BufferedWirter与此类似,不过是分别指定Writer和Reader类型的参数罢了。
一个实例程序:
import java.io.*;
public class Test
{
 public static void main(String[] args)
 {
  try
  {
   FileInputStream in=new FileInputStream(args[0]);
   BufferedInputStream bufIn=new BufferedInputStream(in);
   int limit;
   bufIn.mark(limit=bufIn.available());//在当前位置打上标记
   for(int i=0;i<limit;i++)
    System.out.print((char)(bufIn.read()));
   System.out.println();
   bufIn.reset();//reset缓冲区标志
   int c;
   while((c=bufIn.read())>=0)System.out.print((char)c);
   bufIn.close();
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }
}

5、过滤流之二基本类型数据传输:DataInputStream和DataOutputStream
import java.io.*;
public class Test
{
 public static void main(String args[])
 {
  try
  {
   DataOutputStream dos=new DataOutputStream(
    new BufferedOutputStream(new FileOutputStream(
     "d://javaprogram//w.txt")));
   dos.writeInt(5);
   dos.writeUTF("你好");
   dos.writeBoolean(true);
   dos.writeDouble(3.1415926);
   dos.writeBytes("ok!");
   dos.writeChars("bye bye");
   dos.close();
   DataInputStream dis=new DataInputStream(
    new BufferedInputStream(new FileInputStream(
        "d://javaprogram//w.txt")));
   //读出的顺序应与写入的顺序一致
   System.out.println(dis.readInt());
   System.out.println(dis.readUTF());
   System.out.println(dis.readBoolean());
   System.out.println(dis.readDouble());
   byte b[]=new byte[3];
   dis.readFully(b);
   for(int j=0;j<3;j++)System.out.print((char)b[j]);
   System.out.println();
   StringBuffer st3=new StringBuffer();
   for(int j=0;j<7;j++)st3.append(dis.readChar());
   System.out.println(st3.toString());
   dis.close();
  }
  catch (IOException e)
  {
   System.err.println(e.toString());
  }
 }
}
6、过滤流之三:I/O流的链接图:
  
7、字节和Unicode字符的桥梁:InputStreamReader、OutputStreamWriter
code:
import java.io.*;
public class Test
{
 public static void main(String[] args)throws IOException
 {
  //文件读写
  FileOutputStream fos=new FileOutputStream("w.txt");
  OutputStreamWriter osw=new OutputStreamWriter(fos);
  BufferedWriter bw=new BufferedWriter(osw);
  bw.write("今天是2008年8月3日,离北京奥运还有5天!");
  bw.close();
  FileInputStream fis=new FileInputStream("w.txt");
  InputStreamReader isr=new InputStreamReader(fis);
  BufferedReader br=new BufferedReader(isr);
  System.out.println(br.readLine());
  br.close();
  //控制台读写
  BufferedReader br1=new BufferedReader(new InputStreamReader(System.in));
  BufferedWriter bw1=new BufferedWriter(new OutputStreamWriter(System.out));
  String strLine,str="";
  while((strLine=br1.readLine())!=null)//Ctrl+c结束输入
  {
   str+=strLine;
   System.out.println(strLine);
  }
  br1.close();
  bw1.write(str,0,str.length());
  bw1.write((int)('\n')); //将回车符输入bw1
  bw1.flush();
  bw1.close();
 }
}
8、管道流:PipedInputStream、PipedOutputStream
      作用:用于线程间的通信,一个线程的pipedInputStream对象从另一个线程的PipedOutputStream对象读取输入,要使管道流有用,必须同时构造管道输入流和管道输出流。
例子(孙鑫老师的例子):
import java.io.*;
class Producer extends Thread
{
 private PipedOutputStream pos;
 public Producer(PipedOutputStream pos)
 {
  this.pos=pos;
 }
 public void run()
 {
  try
  {
   pos.write("Hello,welcome you!".getBytes());
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
 }
}
class Consumer extends Thread
{
 private PipedInputStream pis;
 public Consumer(PipedInputStream pis)
 {
  this.pis=pis;
 }
 public void run()
 {
  try
  {
   byte[] buf=new byte[100];
   int len=pis.read(buf);
   System.out.println(new String(buf,0,len));
   pis.close();
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
 }
}
public class Test
{
 public static void main(String args[])
 {
  PipedOutputStream pos=new PipedOutputStream();
  PipedInputStream pis=new PipedInputStream();
  try
  {
   pos.connect(pis);
   new Producer(pos).start();
   new Consumer(pis).start();
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
 }
}
9、PrintWriter类:创建的输出流可以使用print和println方法,按Unicode字符形式输出,输出的数据可读性较好。
        §打印流建立文本文件:
                                PrintWriter out=new PrintWriter(new FileWriter(1.dat));
                                String str="天呢,我告诉你吧:";
                                char[] z={'北','京','奥','运','会','在'};double g=2008;
                                out.println(st);out.print(z);out.print(g);out.println("年08月08日08时08分08秒");
                                out.close();
        §打印流在屏幕上显示文本
                                PrintWriter out=new PrintWriter(System.out);
                                其余同上,此处略
10、文件的随机读写:RandomAccessFile
import java.io.*;
public class Test
{
 public static void main(String args[])throws Exception
 {
  int lineNo;//读到的行号
  long fp;//文件指针
  BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
  RandomAccessFile raf=new RandomAccessFile("w.txt","rw");
  System.out.println("请输入6个字符串");
  int[] len=new int[12];
  String[] str=new String[12];
  for(int i=0;i<6;i++)
  {
   System.out.println("行号"+(i+1)+":");
   str[i]=br.readLine();
   len[i]=str[i].length();
   raf.write((str[i]+"\n").getBytes());
  }
  while(true)
  {
   fp=0;
   raf.seek(0);
   System.out.println("你要显示第几行?"+"(1--6)");
   lineNo=Integer.parseInt(br.readLine());
   for(int i=1;i<lineNo;i++)
    fp=fp+(long)len[i-1]+1;
   raf.seek(fp);
   System.out.println("第"+lineNo+"行"+":"+raf.readLine());
   System.out.println("继续吗?"+"(y/n)");
   if((br.readLine().equals("n")))break;
  }
  raf.seek(raf.length());
  System.out.println("继续写入6行数据:");
  for(int i=6;i<12;i++)
  {
   System.out.println("行号"+(i+1)+":");
   str[i]=br.readLine();
   len[i]=str[i].length();
   raf.write((str[i]+"\n").getBytes());
  }
  System.out.println("打印12行数据:");
  raf.seek(0);
  for(long i=0;i<raf.length();i=raf.getFilePointer())
  {
   System.out.println(raf.readLine());
  }
  raf.close();
 }
}
11、文件的压缩处理
 实例:
import java.io.*;
import java.util.*;
//ZipInputStream和ZipOutputStream在包java.util.zip中
import java.util.zip.*;
public class Test
{
 public static void main(String args[])throws Exception
 {
  //输入若干文件名,将所有文件压缩为w.zip
  ZipOutputStream zos=new ZipOutputStream(
   new BufferedOutputStream(new FileOutputStream("w.zip")));
  for(int i=0;i<args.length;i++)
  {
   BufferedInputStream bis=new BufferedInputStream(
    new FileInputStream(args[i]));
   //将每个要压缩的文件称为一个压缩入口,使用ZipEntry生成压缩入口对象
   //使用putNextEntry(ZipEntry entry)将压缩入口加入到压缩文件
   zos.putNextEntry(new ZipEntry(args[i]));
   int b;
   while((b=bis.read())!=-1)
    zos.write(b);
   bis.close();
  }
  zos.close();
  //解压缩文件并显示
  ZipInputStream zis=new ZipInputStream(
   new BufferedInputStream(new FileInputStream("w.zip")));
  ZipEntry z;
  while((z=zis.getNextEntry())!=null)//获得入口
  {
   System.out.println(z.getName());//显示文件初始名
   int x;
   while((x=zis.read())!=-1)
    System.out.write(x);
   System.out.println();
  }
  zis.close();
 }
}
12、编码与解码
 import java.util.*;
import java.nio.charset.*;
public class Test
{
 public static void main(String args[])throws Exception
 {
  //以下程序段打印当前计算机所能处理的标准 charset
  //Charset类位于java.nio.charset包中,此类定义了用于创建解码器和编码器
  //以及检索与 charset 关联的各种名称的方法。此类的实例是不可变的。
  //Charset.availableCharsets()返回一个映射
  //Map是java.util包中的一个接口
  Map m=Charset.availableCharsets();
  //keySet()方法返回此映射中包含的键的 set 视图
  Set names=m.keySet();
  //构造迭代器访问诸元素
  Iterator it=names.iterator();
  while(it.hasNext())
  {
   System.out.println(it.next());
  }
  //Properties 类位于java.util包中,表示了一个持久的属性集
  //System.getProperties()确定当前的系统属性。
  Properties pps=System.getProperties();
  pps.list(System.out);//打印属性
  //将系统文件的标准字符集改为:ISO-8859-1
  //设置的字符集,只是当前JVM上的字符集
  pps.put("file.encoding","ISO-8859-1");
  int data;
  byte[] buf=new byte[100];
  int i=0;
  while((data=System.in.read())!='q')
  {
   buf[i]=(byte)data;
   i++;
  }
  String str=new String(buf,0,1);
  System.out.println(str);
  //ISO-8859-1字符解码为Unicode字符(java使用)
  //Unicode字符编码为GBK字符
  //但并不是所有字符都能反编码回来,比如汉字将丢失高字节
  String strGBK=new String(str.getBytes("ISO-8859-1"),"GBK");
  System.out.println(strGBK);
 }
} 
13、对象序列化
               对象的寿命常随着对象的程序的终止而终止,倘若需要对对象的状态进行保存,需要时再恢复。我们把对象的这种能够记录自己状态以便将来再生的能力,叫做对象的持续性(persistence)。对象通过写出描述自己状态的值——对象转化为字节流,来记录自己的这个过程叫对象的序列化(serialization)。  一个对象要能够实现序列化,必须实现Serializable接口,或者Externalizable接口。 
               一个对象被序列化时,只保存对象的非静态成员变量,如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。但如果把这个引用标记为transient,那么对象仍然可以序列化。
                另外,对象序列化建立了一张对象网,将当前要序列化的对象中所持有的引用指向的对象都包含起来一起写入到文件,如果一次序列化几个对象,它们中的相同内容会被共享。
code:

import java.io.*;

public class Test
{
 public static void main(String[] args) throws Exception
 {
  Employee e1=new Employee("zhangsan",25,3000.50);
  Employee e2=new Employee("lisi",24,3200.40);
  Employee e3=new Employee("wangwu",27,3800.55);
  ObjectOutputStream oos=new ObjectOutputStream(
   new FileOutputStream("w.dat"));
  oos.writeObject(e1);
  oos.writeObject(e2);
  oos.writeObject(e3);
  oos.close();

  ObjectInputStream ois=new ObjectInputStream(
   new FileInputStream("w.dat"));
  Employee e;
  String strSal;
  for(int i=0;i<3;i++)
  {
   e=(Employee)ois.readObject();
   //设置自已需要的输出方式
   //strSal=(e.salary==0)?"不告诉你":String.valueOf(e.salary);
   //System.out.println(e.name+":"+e.age+":"+strSal);
   System.out.println(e.name+":"+e.age+":"+e.salary);
  }
  ois.close();
 }
}
class Employee implements Serializable
{
 String name;
 int age;
 transient double salary;
 transient Thread t=new Thread();
 public Employee(String name,int age, double salary)
 {
  this.name=name;
  this.age=age;
  this.salary=salary;
 }
 //重写方法,完成需要的操作,如果不重写,要想不写入某些数据可以标记为transient
 //本程序的数据salary就被标记为transient,打印时输出为0.0,如果是String将为null
 /*private void writeObject(java.io.ObjectOutputStream oos)throws IOException
 {
  oos.writeUTF(name);
  oos.writeInt(age);
  //System.out.println("Write Object");
 }
 private void readObject(java.io.ObjectInputStream ois)throws IOException
 {
  name=ois.readUTF();
  age=ois.readInt();
  //System.out.println("Read Object");
 }*/
}

 

分享到:
评论

相关推荐

    读写文件读取文件操作读取文件操作读取文件操作读取文件操作

    同时,文件读取可能涉及缓冲区,即内存中的一块区域,用于暂存数据,提高效率。 四、错误处理与文件权限 在读取文件过程中可能会遇到各种问题,如文件不存在、无权限、文件正在被其他进程使用等。因此,编写代码时...

    易语言大文件读写模块

    通常,初始化可能涉及打开文件、检查文件是否存在、设置读写模式(读取或写入)等步骤。 “取总行数”功能允许开发者获取文件中的总行数,这对于数据统计或分析非常有用。实现这一功能通常需要遍历文件,但为了避免...

    python中的文件读写练习题(csdn)————程序.pdf

    在实际应用中,文件读写操作是非常常见的,例如读取数据文件、写入日志文件、导出报表等。 Python 语言提供了多种文件读写方式,包括文本文件、CSV 文件、Excel 文件等。 读取 CSV 文件 CSV (Comma Separated ...

    Python中使用asyncio 封装文件读写

    在这段示例代码中,`read_step`函数负责执行具体的文件读取操作,并根据读取的结果来决定是否继续调用自身进行下一轮读取。一旦读取完成,就设置`Future`对象的结果。`read`函数则用于初始化读取操作,并返回一个`...

    c++文件读写.pdf

    // 从文件读取 1024 字节的数据 file2.write(buffer, 1024); // 向文件写入 1024 字节的数据 在实际应用中,根据需要的不同,选择不同的类来定义:如果想以输入方式打开文件,就用 ifstream 来定义;如果想以输出...

    大文件读写内存映射.rar

    3. 访问数据:映射成功后,你可以像操作普通数组一样访问文件数据。例如,使用索引访问特定位置的数据,或者通过迭代器遍历整个映射区域。 4. 读写操作:对于读取操作,可以直接读取映射区域的值;对于写入操作,...

    linux c 配置文件读写

    3. **读取文件**:如果以读模式打开文件,可以使用`fgets()`读取每一行,然后解析行内容,根据分隔符(通常是等号)将节、键和值分开。例如,可以使用`strtok()`函数进行字符串分割。 4. **解析数据**:将读取到的...

    STL文件的读取显示.zip_STL c++_STL文件_STL读取_读取stl_读取stl文件

    以下是对STL文件读取和显示的详细知识讲解。 首先,了解STL文件的基本结构。STL文件分为两种类型:ASCII和二进制。ASCII格式的STL文件内容可读性较强,但文件大小较大;二进制格式则更紧凑,读写速度更快。每个STL...

    文件读写监控工具文件读写监控工具文件读写监控工具

    文件读写监控工具是计算机系统管理和维护中不可或缺的软件,它们可以帮助用户跟踪、记录和分析系统中的文件操作,包括打开、创建、修改、删除等动作。这类工具在故障排查、性能优化、安全审计等方面有着广泛的应用。...

    带密码INI文件读写_ini文件_especiallyvsy_加密_读写ini文件_文件读取_

    “文件读取”标签则进一步说明了整个过程涉及到文件的读取操作,这通常包括定位文件、打开文件、读取数据、关闭文件等步骤。在加密ini文件的上下文中,这意味着在读取文件时需要先解密数据,然后才能将其转换成程序...

    C#进行dat文件读写操作

    3. **C# dat文件写操作** 写入dat文件的步骤与读取类似,只是使用`StreamWriter`(文本)或`BinaryWriter`(二进制)来替换`StreamReader`和`BinaryReader`。以下是一个写入文本数据的例子: ```csharp using ...

    Android读写配置文件

    3. **创建和写入JSON文件**: - 同样,使用`FileOutputStream`或`BufferedWriter`创建文件,然后使用Gson或Jackson库将对象转换为JSON字符串并写入文件。 - 如果数据结构简单,可以直接手动构建JSON字符串。 4. *...

    csv文件读写操作

    在MFC(Microsoft Foundation Classes)环境下,利用Visual Studio 2015进行CSV文件的读写操作,可以方便地处理这类数据。本文将详细介绍如何在VS2015的MFC项目中实现CSV文件的读写功能。 1. **CSV文件结构** CSV...

    TDMS文件读写【官方C语言例程】

    3. **读取TDMS文件**: 读取过程涉及打开TDMS文件,找到需要的通道,然后读取数据。这通常通过`TDMSFileOpen`打开文件,`TDMSGetChannel`获取通道,以及`TDMSReadAnalogF64`或`TDMSReadDigitalU32`读取数据。注意,...

    用C语言读写SGY格式的地震数据文件-苏.rar_C语言;读写SGY格式文件_sgy_sgy文件读取_sgy读写_读写sgy文件

    在C语言中读取SGY文件,首先需要打开文件,可以使用`fopen()`函数,指定文件路径和打开模式(如"rb"表示以二进制读取)。接着,读取头信息,这通常涉及读取固定大小的块,并解析其中的各个字段。例如,使用`fread()`...

    c# 文件读写类,可以用于读取更改定点的文件

    c# 文件读写类,可以用于读取更改定点的文件

    codesys读写CVS文件(亲测)

    另外需要读取txt文件的关注本人往期的文章,有讲解。资源内部包含工程文件以及CVS文件。 如果是打开工程的界面不显示,可能是codesys的版本过低,需要更新版本。可以参见博客文章:codesys工程ST语言学习笔记(六)...

    QT实现wav音频文件读写

    在QT中处理音频文件,我们可以利用其提供的多媒体模块来实现对音频文件的读写操作。本话题将重点讨论如何使用QT实现PCM(脉冲编码调制)与WAV音频文件的相互转换,以及如何读取和写入WAV音频格式。 首先,WAV是一种...

    基于C++的VTK对不同格式的文件进行读取与另存源码和用到的素材文件.txt

    本资源包含基于C++的VTK对不同格式的文件进行读取与另存源码和用到的素材文件。 实例1:读取STL文件并渲染显示...本资源配套CSDN博客“基于C++的VTK不同格式文件读取与另存整理”,可 前往查看具体原理和实现效果!!!

Global site tag (gtag.js) - Google Analytics